---
name: seo-meta
description: |
Generate complete SEO meta tags for every page. Covers title patterns, meta descriptions, Open Graph, Twitter Cards, and JSON-LD structured data (LocalBusiness, Service, FAQ, BreadcrumbList).
Use when: building pages, adding social sharing, implementing structured data, optimizing for search engines.
license: MIT
metadata:
keywords:
- seo
- meta-tags
- open-graph
- twitter-cards
- structured-data
- json-ld
- schema-org
last_updated: 2026-01-14
version: 1.0.0
---
# SEO Meta Tags
**Status**: Production Ready ✅
**Last Updated**: 2026-01-14
**Source**: Schema.org, Open Graph Protocol, Twitter Developer Docs
---
## Quick Start
Every page needs:
```tsx
{/* Basic SEO */}
Service in Location | Brand Name
{/* Open Graph */}
{/* Twitter Card */}
{/* JSON-LD Structured Data */}
```
---
## Title Tag Patterns
**Character Limits**:
- Google: 50-60 characters (desktop), 78 (mobile)
- Social: 60-70 characters
**Page Type Formulas**:
| Page Type | Pattern | Example |
|-----------|---------|---------|
| Home | [Brand] - [Primary Service] in [Location] | Acme Plumbing - 24/7 Emergency Plumber Sydney |
| Service | [Service] in [Location] \| [Brand] | Hot Water Repairs Sydney \| Acme Plumbing |
| Location | [Service] [Suburb] \| [Brand] | Plumber Bondi \| Acme Plumbing |
| About | About [Brand] - [Tagline/USP] | About Acme - Licensed Plumbers Since 1995 |
| Contact | Contact [Brand] - [Location] \| [Phone] | Contact Acme Plumbing - Sydney \| 1300 XXX XXX |
**Title Modifiers** (add credibility):
- 24/7 Emergency
- Licensed & Insured
- Free Quotes
- Same Day Service
- Family Owned
- Award Winning
**Anti-Patterns** (avoid):
- ❌ "Welcome to..." (wastes characters)
- ❌ Keyword stuffing (plumber plumbing plumbers)
- ❌ ALL CAPS (looks spammy)
- ❌ Special characters (★ § ¶)
---
## Meta Description Patterns
**Character Limits**:
- Desktop: 155-160 characters
- Mobile: 120-130 characters
**Formula**:
```
[Value prop] [Service] in [Location]. [Differentiator]. [CTA].
```
**Examples by Page Type**:
**Home Page**:
```
Fast, reliable plumbing services in Sydney. 24/7 emergency response, licensed plumbers, upfront pricing. Call 1300 XXX XXX for same-day service.
```
**Service Page**:
```
Expert hot water repairs in Sydney. Fix or replace electric, gas & solar systems. Licensed technicians, 1-year warranty. Book online or call 1300 XXX XXX.
```
**Location Page**:
```
Trusted plumber in Bondi. Blocked drains, leaks, hot water, gas fitting. Same-day service, upfront quotes. Call your local plumber on 1300 XXX XXX.
```
**Power Words** (use sparingly):
- Trust: Licensed, Certified, Insured, Guaranteed, Trusted
- Speed: Fast, Quick, Same Day, Emergency, 24/7, Instant
- Value: Affordable, Competitive, Upfront, No Hidden Fees, Free Quote
- Local: Local, Nearby, Your Area, [Suburb Name]
- Quality: Expert, Professional, Experienced, Award Winning
---
## Open Graph Tags
**Required Tags**:
```html
```
**Image Requirements**:
- Dimensions: 1200x630px (1.91:1 ratio)
- Format: JPG or PNG (JPG preferred for file size)
- File size: <1MB (ideally <300KB)
- Text overlay: Keep important text center (safe zone: 1000x530)
**og:type Values by Page Type**:
| Page Type | og:type |
|-----------|---------|
| Home, Service, Location | website |
| Blog Post | article |
| Business Profile | business.business |
**Optional but Recommended**:
```html
```
---
## Twitter Cards
**Card Types**:
| Type | Use Case |
|------|----------|
| summary | Small square image (1:1), basic info |
| summary_large_image | Large image (1.91:1), most common |
**Required Tags**:
```html
```
**Optional**:
```html
```
**Fallback Behavior**:
- If twitter:title missing, uses og:title
- If twitter:description missing, uses og:description
- If twitter:image missing, uses og:image
**Best Practice**: Define og:* tags first, only add twitter:* if values differ.
---
## JSON-LD Structured Data
### LocalBusiness Schema (Most Important)
Use for homepage and contact page:
```json
{
"@context": "https://schema.org",
"@type": "Plumber",
"name": "Acme Plumbing",
"description": "Licensed plumbing services in Sydney",
"@id": "https://acmeplumbing.com.au",
"url": "https://acmeplumbing.com.au",
"logo": "https://acmeplumbing.com.au/logo.png",
"image": "https://acmeplumbing.com.au/og-image.jpg",
"telephone": "+61-XXX-XXX-XXX",
"email": "info@acmeplumbing.com.au",
"priceRange": "$$",
"address": {
"@type": "PostalAddress",
"streetAddress": "123 Main Street",
"addressLocality": "Sydney",
"addressRegion": "NSW",
"postalCode": "2000",
"addressCountry": "AU"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": -33.8688,
"longitude": 151.2093
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "07:00",
"closes": "17:00"
}
],
"sameAs": [
"https://facebook.com/acmeplumbing",
"https://instagram.com/acmeplumbing"
]
}
```
**Specific Business Types** (instead of generic LocalBusiness):
- Plumber, Electrician, Locksmith, HVAC (HVACBusiness)
- Dentist, Attorney, Accountant
- Restaurant, Cafe, FoodEstablishment
- Store, AutoRepair, BeautySalon
### Service Schema
Use for service pages:
```json
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Hot Water Repairs",
"description": "Fast hot water system repairs in Sydney",
"provider": {
"@type": "Plumber",
"name": "Acme Plumbing",
"url": "https://acmeplumbing.com.au"
},
"areaServed": {
"@type": "City",
"name": "Sydney"
},
"availableChannel": {
"@type": "ServiceChannel",
"serviceUrl": "https://acmeplumbing.com.au/hot-water-repairs",
"servicePhone": "+61-XXX-XXX-XXX"
}
}
```
### FAQ Schema (Rich Snippets)
Use for FAQ sections:
```json
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How much does a plumber cost in Sydney?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Plumbing rates in Sydney typically range from $100-$150 per hour for standard work. Emergency callouts may incur higher rates. We provide upfront quotes before starting work."
}
},
{
"@type": "Question",
"name": "Do you offer same-day service?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, we offer same-day plumbing service across Sydney for urgent repairs. Call us before 2pm for same-day availability."
}
}
]
}
```
### BreadcrumbList Schema
Use on all pages except homepage:
```json
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://acmeplumbing.com.au"
},
{
"@type": "ListItem",
"position": 2,
"name": "Services",
"item": "https://acmeplumbing.com.au/services"
},
{
"@type": "ListItem",
"position": 3,
"name": "Hot Water Repairs",
"item": "https://acmeplumbing.com.au/hot-water-repairs"
}
]
}
```
---
## Canonical URLs
**When to Use**:
- Every page should have a self-referencing canonical
- Duplicate content (pagination, filters, print versions)
- Syndicated content
- Cross-domain duplicates
**Self-Referencing Canonical**:
```html
```
**Pagination**:
```html
```
**Common Mistakes**:
- ❌ Missing canonical (Google chooses for you)
- ❌ Relative URLs (use absolute URLs)
- ❌ Canonical pointing to different content
- ❌ Multiple canonicals (only one per page)
---
## Validation Tools
**Check Your Implementation**:
| Tool | Purpose | URL |
|------|---------|-----|
| Google Rich Results Test | Test structured data | search.google.com/test/rich-results |
| Schema Markup Validator | Validate JSON-LD | validator.schema.org |
| Facebook Debugger | Test Open Graph tags | developers.facebook.com/tools/debug |
| Twitter Card Validator | Test Twitter Cards | cards-dev.twitter.com/validator |
| Screaming Frog | Audit all pages | screamingfrog.co.uk/seo-spider |
**Browser Extensions**:
- SEO Meta in 1 Click (Chrome)
- META SEO inspector (Firefox)
---
## Quick Reference Checklist
For every page, include:
- [ ] `` (50-60 chars, unique per page)
- [ ] `` (150-160 chars, unique per page)
- [ ] `` (absolute URL)
- [ ] Open Graph tags (og:title, og:description, og:image, og:url, og:type)
- [ ] Twitter Card tags (twitter:card, twitter:title, twitter:description, twitter:image)
- [ ] JSON-LD structured data (LocalBusiness on homepage, Service on service pages)
- [ ] BreadcrumbList schema (all pages except homepage)
- [ ] Mobile viewport meta tag
- [ ] Charset meta tag (UTF-8)
**Never**:
- ❌ Duplicate titles across pages
- ❌ Use "Welcome to..." in titles
- ❌ Omit og:image (critical for social sharing)
- ❌ Use generic @type (LocalBusiness instead of Plumber)
- ❌ Skip CTA in meta description
---
## Error Prevention
### Common Issues
| Issue | Cause | Fix |
|-------|-------|-----|
| No rich snippets in search | Invalid JSON-LD | Use validator.schema.org, check commas/quotes |
| Social share shows wrong image | og:image missing or wrong size | Use 1200x630px, test with Facebook Debugger |
| Title truncated in search | Too long | Keep under 60 chars |
| Description truncated | Too long | Keep under 160 chars |
| Multiple pages rank for same keyword | Duplicate titles | Make each title unique |
### Testing Workflow
1. **Validate HTML**: Use W3C validator
2. **Test Structured Data**: Google Rich Results Test
3. **Test Social Sharing**: Facebook Debugger + Twitter Card Validator
4. **Mobile Preview**: Google Search Console URL Inspection
5. **Cross-Browser Check**: Test meta rendering in Chrome/Firefox/Safari
---
## Best Practices Summary
**Title Tags**:
- 50-60 characters maximum
- Include primary keyword + location + brand
- Unique for every page
- Use modifiers (24/7, Licensed, Free) sparingly
**Meta Descriptions**:
- 150-160 characters maximum
- Include value prop + differentiator + CTA
- Write for humans, not search engines
- Every page needs unique description
**Open Graph**:
- Always include og:image (1200x630px)
- Use absolute URLs
- Keep og:title under 60 chars
- Test with Facebook Debugger before launch
**JSON-LD**:
- Use specific @type (Plumber, not LocalBusiness)
- Include all contact info (phone, address, email)
- Add openingHoursSpecification for better display
- Validate with schema.org validator
**Canonical URLs**:
- Every page needs canonical
- Always use absolute URLs
- Self-referencing is good practice
- Only one canonical per page
---
**Production Notes**:
- Use react-helmet-async for React apps (SSR-safe)
- Generate JSON-LD dynamically from CMS/database
- Cache meta tag components for performance
- Monitor Search Console for indexing issues
- Update structured data when business details change