Add JSON-LD schema markup to WordPress without a plugin

Schema markup is structured data that you add to your pages to help search engines understand your content and display rich results in search listings — star ratings, FAQ dropdowns, breadcrumbs, event dates, and recipe cards. Google uses the JSON-LD format injected into the <head> of your pages as its preferred method, and adding it to WordPress without a plugin is straightforward via a wp_head action hook. The most impactful schema types for a typical blog are Article (for posts), BreadcrumbList (improves navigation in search results), WebSite with a SearchAction (enables the sitelinks search box in Google), and FAQPage (expands FAQ content directly in search results with zero extra clicks required). Each schema type is a JSON object with a @context pointing to https://schema.org and a @type matching the schema type name. WordPress provides the data you need through its own API: get_the_title(), get_the_date(), get_the_modified_date(), get_avatar_url(), and get_the_author_meta(). Validating your markup with Google’s Rich Results Test (search.google.com/test/rich-results) before deploying is essential — invalid schema is worse than no schema because Google may penalise misleading structured data. Pair this with the canonical URL tag and Open Graph tags for a complete technical SEO setup. The snippet below injects Article schema on single posts.

Problem: You want to add JSON-LD schema markup to WordPress posts and pages to enable rich results in Google Search, without installing a full SEO plugin.

Solution: Add the following code to your functions.php file:

add_action( 'wp_head', 'helloadmin_schema_markup' );
function helloadmin_schema_markup() {

    // WebSite schema (output on every page)
    $website_schema = [
        '@context' => 'https://schema.org',
        '@type'    => 'WebSite',
        'name'     => get_bloginfo( 'name' ),
        'url'      => home_url( '/' ),
        'potentialAction' => [
            '@type'       => 'SearchAction',
            'target'      => [
                '@type'       => 'EntryPoint',
                'urlTemplate' => home_url( '/?s={search_term_string}' ),
            ],
            'query-input' => 'required name=search_term_string',
        ],
    ];
    echo '<script type="application/ld+json">' . wp_json_encode( $website_schema ) . '</script>' . "
";

    // Article schema (single posts only)
    if ( ! is_single() ) {
        return;
    }
    $author_id = get_the_author_meta( 'ID' );
    $article_schema = [
        '@context'         => 'https://schema.org',
        '@type'            => 'Article',
        'headline'         => get_the_title(),
        'url'              => get_permalink(),
        'datePublished'    => get_the_date( 'c' ),
        'dateModified'     => get_the_modified_date( 'c' ),
        'author'           => [
            '@type' => 'Person',
            'name'  => get_the_author(),
            'url'   => get_author_posts_url( $author_id ),
        ],
        'publisher' => [
            '@type' => 'Organization',
            'name'  => get_bloginfo( 'name' ),
            'logo'  => [
                '@type' => 'ImageObject',
                'url'   => get_site_icon_url( 60 ),
            ],
        ],
        'description' => wp_strip_all_tags( get_the_excerpt() ),
    ];
    echo '<script type="application/ld+json">' . wp_json_encode( $article_schema ) . '</script>' . "
";
}

NOTE: Google requires the datePublished and dateModified dates to be in ISO 8601 format — that is why get_the_date( ‘c’ ) is used (‘c’ is PHP’s ISO 8601 date format constant). If your site icon is not set, get_site_icon_url() returns an empty string, which will cause a validation warning — either set a site icon in Appearance → Customize → Site Identity or remove the logo key from the publisher object before deploying.