All skills
asyrafhussin avatar

/seo-best-practices

@46bae0b

SEO patterns, conventions, and audit for web applications. Use when implementing meta tags, structured data, Core Web Vitals, sitemaps, Open Graph, auditing SEO, or optimizing pages for search engines. Triggers on "audit SEO", "check SEO", "review SEO", or tasks involving search optimization, schema markup, or social sharing meta tags.

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/seo-best-practices

This session only. Nothing lands on disk.

rulesschema-validation.md

≈2.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Structured Data Validation and Monitoring

Impact: HIGH (Invalid schema silently fails — broken markup gets zero rich results)

Structured data errors are completely silent — there is no browser console warning when your JSON-LD has a missing required field or a trailing comma. The only way to catch issues is through proactive validation and monitoring. Without a validation workflow, broken schema can go undetected for months while competitors capture your rich result slots.

Incorrect

// ❌ Deploying schema without any validation or monitoring
class BlogPostController extends Controller
{
    public function show(Post $post)
    {
        // Schema built inline, never tested, never validated
        return Inertia::render('Blog/Show', [
            'post' => $post,
            // No one checks if this produces valid JSON-LD
            // No tests verify schema presence
            // Search Console errors go unchecked for months
        ]);
    }
}

Problems:

  • No automated tests verify that JSON-LD is present and parseable on rendered pages
  • No pre-deployment validation catches missing required fields or syntax errors
  • Search Console enhancement reports are never reviewed — errors accumulate silently
  • A template change can break schema across hundreds of pages with no alert

Correct

1. Validate Before Deploying

Always test structured data with Google's Rich Results Test before deploying changes. Paste the rendered HTML or a live URL to verify all schema types are detected and have no errors or warnings.

2. Write Automated Tests

// ✅ Laravel feature test verifying Article schema is present and valid
namespace Tests\Feature;

use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class BlogPostSchemaTest extends TestCase
{
    use RefreshDatabase;

    public function test_blog_post_has_valid_article_schema(): void
    {
        $post = Post::factory()->create();

        $response = $this->get(route('posts.show', $post));

        $response->assertStatus(200);
        $response->assertSee('"@type":"Article"', false);
        $response->assertSee('"headline"', false);
    }

    public function test_blog_post_schema_contains_required_fields(): void
    {
        $author = User::factory()->create(['name' => 'Jane Smith']);
        $post = Post::factory()->create([
            'title' => 'Test Post Title',
            'user_id' => $author->id,
        ]);

        $response = $this->get(route('posts.show', $post));
        $content = $response->getContent();

        // Extract JSON-LD blocks from rendered HTML
        preg_match_all(
            '/<script type="application\/ld\+json">(.*?)<\/script>/s',
            $content,
            $matches
        );

        $this->assertNotEmpty($matches[1], 'No JSON-LD blocks found on page');

        $hasArticle = false;
        foreach ($matches[1] as $jsonLd) {
            $data = json_decode($jsonLd, true);
            $this->assertNotNull($data, 'JSON-LD block is not valid JSON');

            // Check @graph array or single type
            $entities = isset($data['@graph']) ? $data['@graph'] : [$data];

            foreach ($entities as $entity) {
                if (($entity['@type'] ?? '') === 'Article') {
                    $hasArticle = true;
                    $this->assertArrayHasKey('headline', $entity);
                    $this->assertArrayHasKey('datePublished', $entity);
                    $this->assertArrayHasKey('author', $entity);
                    $this->assertArrayHasKey('image', $entity);
                    $this->assertEquals('Test Post Title', $entity['headline']);
                }
            }
        }

        $this->assertTrue($hasArticle, 'No Article schema found in JSON-LD');
    }

    public function test_faq_page_has_valid_faq_schema(): void
    {
        $response = $this->get(route('faq.index'));

        $response->assertStatus(200);
        $response->assertSee('"@type":"FAQPage"', false);
        $response->assertSee('"mainEntity"', false);
        $response->assertSee('"acceptedAnswer"', false);
    }
}

3. Monitor Search Console Weekly

Check Google Search Console Enhancements reports at least weekly for schema errors:

  • Navigate to Search Console > Enhancements and review each structured data type
  • Look for Error and Warning counts — both prevent rich results
  • Click into specific issues to see affected URLs
  • After fixing, use Validate Fix to request re-crawling

4. Common Validation Errors

Error Cause Fix
Missing required field image Article schema without image property Add at least one image URL to the image array
Invalid date format Using "March 10, 2026" instead of ISO 8601 Use "2026-03-10T08:00:00+00:00" or ->toIso8601String()
URL is not absolute Using /blog/my-post instead of full URL Use url() or route() helpers to generate absolute URLs
Content mismatch Schema headline differs from visible <h1> Ensure schema values match on-page content exactly
Trailing comma in JSON {"name": "Acme",} — invalid JSON syntax Use json_encode() in PHP or JSON.stringify() in JS instead of manual strings
author is a string "author": "Jane" instead of a Person object Use {"@type": "Person", "name": "Jane"}
Missing @context JSON-LD without "@context": "https://schema.org" Always include @context at the top level or in @graph wrapper

5. CI Pipeline Validation

// ✅ Base test helper for reusable JSON-LD validation
namespace Tests;

use Illuminate\Testing\TestResponse;

trait ValidatesJsonLd
{
    /**
     * Assert that a response contains valid JSON-LD with the given @type.
     */
    protected function assertHasJsonLdType(TestResponse $response, string $type): array
    {
        $content = $response->getContent();

        preg_match_all(
            '/<script type="application\/ld\+json">(.*?)<\/script>/s',
            $content,
            $matches
        );

        $this->assertNotEmpty($matches[1], 'No JSON-LD blocks found');

        foreach ($matches[1] as $jsonLd) {
            $data = json_decode($jsonLd, true);
            $this->assertNotNull($data, "Invalid JSON in JSON-LD block: {$jsonLd}");

            $entities = isset($data['@graph']) ? $data['@graph'] : [$data];

            foreach ($entities as $entity) {
                if (($entity['@type'] ?? '') === $type) {
                    return $entity;
                }
            }
        }

        $this->fail("No JSON-LD entity with @type \"{$type}\" found");
    }
}
// ✅ React: verify schema is rendered correctly in development
import { Head } from '@inertiajs/react';

interface SchemaScriptProps {
  schema: Record<string, unknown>;
}

export function SchemaScript({ schema }: SchemaScriptProps) {
  const jsonString = JSON.stringify(schema);

  // Validate in development — catch issues before they reach production
  if (import.meta.env.DEV) {
    try {
      const parsed = JSON.parse(jsonString);
      if (!parsed['@context'] && !parsed['@graph']) {
        console.warn('[Schema] Missing @context in JSON-LD:', parsed);
      }
    } catch {
      console.error('[Schema] Invalid JSON-LD:', jsonString);
    }
  }

  return (
    <Head>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: jsonString }}
      />
    </Head>
  );
}

Benefits:

  • Automated tests catch schema regressions on every pull request before they reach production
  • The ValidatesJsonLd trait makes it easy to add schema assertions to any feature test
  • Weekly Search Console monitoring catches issues that automated tests cannot (e.g., crawl-time rendering differences)
  • Development-mode validation in React gives immediate console feedback during local development

Reference: Google Rich Results Test

Source: SKILL.md on GitHub

1 warning16d4 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill is a comprehensive SEO reference and audit tool for React and Laravel applications. It provides detailed patterns for optimizing Core Web Vitals, technical SEO, and structured data. No malicious code, obfuscation, or data exfiltration patterns were identified. The skill is designed to analyze project code and provide structural feedback based on a predefined checklist.

  • Socket16d

    5 alerts: gptAnomaly

  • Snyk16d

    Risk: LOW · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 46bae0b. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 6 months ago
metadata
{
  "author": "agent-skills",
  "version": "1.2.0"
}

README badge

README badge for asyrafhussin/agent-skills/seo-best-practices