-
Start Dev Server
npm run dev
-
Open Validation Tool
http://localhost:5173/seo-validator.html -
Test Different Pages
- Change the path input (e.g.,
/topics/present-simple) - Click "Run SEO Validation"
- Check all green ✓ marks
- Change the path input (e.g.,
URL: http://localhost:5173/
Open DevTools (F12) → Elements → <head>
Verify:
-
<title>contains "TypoGrammar" -
<meta name="description">exists (only ONE) -
<link rel="canonical" href="https://typogrammar.com/">(only ONE) -
<meta property="og:title">exists -
<meta property="og:description">exists -
<meta property="og:url">= "https://typogrammar.com/" -
<meta property="og:image">exists -
<meta name="twitter:card">= "summary_large_image" - At least 2
<script type="application/ld+json">(WebSite + Organization)
Run in Console:
// Should all return 1
document.querySelectorAll('meta[name="description"]').length
document.querySelectorAll('link[rel="canonical"]').length
document.querySelectorAll('title').length
// Should return 2 or more
document.querySelectorAll('script[type="application/ld+json"]').lengthURL: http://localhost:5173/topics/present-simple
Verify:
- Title: "Present Simple | TypoGrammar" (humanized from slug)
- Description exists (auto-generated)
- Canonical: "https://typogrammar.com/topics/present-simple"
- All meta tags present
- No errors in console
URL: http://localhost:5173/ (HomePage uses usePageMetadata)
Verify:
- Custom title from usePageMetadata is used (not overridden)
- Custom description from usePageMetadata is used
- OG tags reflect the custom values
- No duplicate meta tags
Run in Console on ANY page:
// All should return 1 (not 2 or more)
console.log('Descriptions:', document.querySelectorAll('meta[name="description"]').length);
console.log('Canonicals:', document.querySelectorAll('link[rel="canonical"]').length);
console.log('Titles:', document.querySelectorAll('title').length);
// Should return 2+ (WebSite + Organization + optional others)
console.log('JSON-LD Scripts:', document.querySelectorAll('script[type="application/ld+json"]').length);Expected Output:
Descriptions: 1
Canonicals: 1
Titles: 1
JSON-LD Scripts: 2 (or more)
npm run buildCheck:
- No TypeScript errors
- No build errors
- dist/ folder created
- File sizes acceptable (SEO adds ~5-10KB)
URL: https://developers.facebook.com/tools/debug/
Steps:
- Enter:
https://typogrammar.com/ - Click "Debug"
- Verify:
- Title appears correctly
- Description appears correctly
- Image preview shows (if og:image set)
URL: https://cards-dev.twitter.com/validator
Steps:
- Enter:
https://typogrammar.com/ - Click "Preview Card"
- Verify:
- Card displays correctly
- Title, description, image present
URL: https://search.google.com/test/rich-results
Steps:
- Enter:
https://typogrammar.com/ - Wait for analysis
- Verify:
- WebSite schema detected
- Organization schema detected
- No errors or warnings
URL: https://validator.schema.org/
Steps:
- Copy JSON-LD from page source
- Paste into validator
- Verify:
- No errors
- All required fields present
<!-- Only ONE of each -->
<title>Present Simple | TypoGrammar</title>
<meta name="description" content="Learn Present Simple...">
<link rel="canonical" href="https://typogrammar.com/topics/present-simple">
<!-- Complete OG tags -->
<meta property="og:title" content="...">
<meta property="og:description" content="...">
<meta property="og:url" content="...">
<meta property="og:image" content="...">
<meta property="og:site_name" content="TypoGrammar">
<!-- Complete Twitter tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="...">
<meta name="twitter:description" content="...">
<meta name="twitter:image" content="...">
<!-- At least 2 JSON-LD scripts -->
<script type="application/ld+json" data-seo="true">
{"@type": "WebSite", ...}
</script>
<script type="application/ld+json" data-seo="true">
{"@type": "Organization", ...}
</script><!-- Duplicates (BAD) -->
<title>...</title>
<title>...</title> ❌
<meta name="description" content="...">
<meta name="description" content="..."> ❌
<link rel="canonical" href="...">
<link rel="canonical" href="..."> ❌
<!-- Missing tags (BAD) -->
<meta property="og:title"> <!-- No content attribute ❌ -->
<meta name="twitter:card" content=""> <!-- Empty content ❌ -->
<!-- Broken JSON-LD (BAD) -->
<script type="application/ld+json">
{Invalid JSON} ❌
</script>Check:
- Is dev server running? (
npm run dev) - Is Layout.tsx rendering? (check browser DevTools)
- Are there errors in console? (F12 → Console)
Fix:
# Restart dev server
npm run devCheck:
// Run in console
document.querySelectorAll('meta[name="description"]').lengthIf > 1:
- Both global SEO and usePageMetadata are creating tags
- This is expected behavior (usePageMetadata should take priority)
- Verify the custom value is used, not the default
Check:
- Does location.pathname change in React DevTools?
- Is SEO component re-rendering?
Fix:
- SEO component depends on
props.path - Ensure Layout.tsx passes
location.pathname
Check:
// Run in console
Array.from(document.querySelectorAll('script[type="application/ld+json"]')).forEach(s => {
try {
JSON.parse(s.textContent);
console.log('✓ Valid JSON');
} catch (e) {
console.error('✗ Invalid JSON:', e);
}
});Fix:
- Check buildSeoMeta.ts for JSON structure
- Ensure all strings are properly escaped
- ❌ No canonical URLs
- ❌ Missing OG tags
- ❌ No Twitter cards
- ❌ No structured data
- ❌ Inconsistent titles
- ❌ Manual meta tags per page
- ✅ Canonical URLs on ALL pages
- ✅ Complete OG tags
- ✅ Twitter cards configured
- ✅ WebSite + Organization schemas
- ✅ Auto-generated titles
- ✅ Global system (one line in Layout.tsx)
- All 6 SEO files created in
src/seo/ - SEO component created in
src/components/SEO.tsx - Layout.tsx updated with
<SEO />component - No TypeScript errors
- Build succeeds (
npm run build) - Homepage SEO validated
- Topic page SEO validated
- No duplicate meta tags
- JSON-LD schemas present
- Console has no errors
- Backward compatible (usePageMetadata still works)
Status: Ready for Production ✅
Date: January 13, 2026
Next Step: Deploy to production and test live URLs with external validators