on October 1, 2026
If you’re running Yoast SEO on WordPress, you already have structured data – Yoast generates a solid baseline automatically: your Organization, your WebSite, a BreadcrumbList on every page. What it can’t do is know about the specific content on your specific pages: which paragraphs are FAQ answers, which blocks are client testimonials, which pages describe an actual service you sell. That part is still on you.
What Yoast gives you, and where it stops
Inspect the <head> of almost any Yoast-powered page and you’ll find a yoast-schema-graph script block with WebPage, WebSite, Organization, and breadcrumb entities already wired together correctly. That’s genuinely useful and you shouldn’t try to duplicate it. What it does not include, because Yoast has no way to know: Service schema describing what you actually offer, FAQPage schema built from your real FAQ content, or Review schema built from real client testimonials. Those need page-specific code, hooked into wp_head alongside Yoast’s own output.
Service schema, the simple case
If you have dedicated service pages, this is mostly a matter of reading the page’s own title and meta description and wrapping them:
function output_service_schema() {
global $post;
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'Service',
'serviceType' => get_the_title( $post ),
'name' => get_the_title( $post ),
'description' => get_post_meta( $post->ID, '_yoast_wpseo_metadesc', true ),
'url' => get_permalink( $post ),
'provider' => array(
'@type' => 'Organization',
'name' => 'Your Company',
'url' => home_url( '/' ),
),
);
echo '<script type="application/ld+json">' . wp_json_encode( $schema ) . '</script>';
}
FAQPage schema from content you already wrote
This one is more interesting: rather than maintaining FAQ schema by hand in a second place, parse it out of the FAQ content itself. If your FAQ answers already follow a consistent pattern – a bolded, numbered question followed by its answer paragraph – a single regex can turn the whole page into valid FAQPage entities without a duplicate data-entry step:
preg_match_all(
'/<p><strong>\s*\d+\.\s*(.*?)<\/strong>\s*<br\s*\/?>\s*(.*?)<\/p>/is',
$content,
$matches,
PREG_SET_ORDER
);
Each match becomes one Question / acceptedAnswer pair. The advantage of parsing existing content instead of hand-authoring separate schema: the schema can never drift out of sync with what visitors actually read on the page, because it’s generated from the same source.
Review schema, and the one thing not to do
Real client testimonials, parsed the same way, become Review entities with an author and a reviewBody. It’s tempting to go one step further and add AggregateRating on top – Google’s rich-result eligibility for review stars depends on it. Don’t, unless you have genuine, verifiable numeric ratings behind each testimonial. Google’s own structured data guidelines are explicit that self-serving or fabricated ratings are a violation, and it’s the kind of thing that gets a whole site’s rich results disabled at once, not just the one page. A page of real, named, quoted testimonials without a star rating is honest and still useful. A fabricated 4.9/5 average is not worth the risk.
Worth doing on your own site
None of this replaces Yoast – it sits alongside it, filling in the specific gaps Yoast can’t. If your site has real FAQ content, real testimonials, or real service pages that aren’t showing up with rich structured data, get in touch and I can add this properly.