Skip to content

feat(seo+geo): add Schema.org structured data#120

Merged
prudentbird merged 5 commits into
devfrom
feat/seo-109-structured-data
Jun 5, 2026
Merged

feat(seo+geo): add Schema.org structured data#120
prudentbird merged 5 commits into
devfrom
feat/seo-109-structured-data

Conversation

@prudentbird

Copy link
Copy Markdown
Member

Summary

Emits JSON-LD structured data so search engines and AI answer engines (ChatGPT, Perplexity, Google AI Overviews) can confidently identify the product, the organization, and the offering.

  • Site-wide @graph rendered from the root layout: Organization + WebSite + SoftwareApplication (cross-linked via @id).
  • Organization mirrors/extends Ajared coverage: legal name, two locations (Toronto & Abuja with PostalAddress + GeoCoordinates), ContactPoint (innovation@ajared.ca), logo, and sameAs (ajared.ng, ajared.ca, X, GitHub).
  • SoftwareApplication describes Retailytics — BusinessApplication, Offer (free trial), and a featureList.
  • FAQPage emitted on the home page from the existing FAQ content (single source).
  • BreadcrumbList on /contact, /privacy, /terms via SitePage.

Implemented as plain builders in src/lib/structured-data.ts + a tiny JsonLd component.

Done when (issue #109)

  • JSON-LD on every page describing organization, website, product/service, key features, and FAQs.
  • Entity coverage at least at parity with ajared.ca / ajared.ng (organization, locations, contact, services/features, breadcrumbs, FAQ).

Validation

Notes

Stacked on #108 (targets feat/seo-108-sitemap). Organization.logo currently points at the generated OG image; swap for a dedicated square logo when one exists.

Closes #109

Every public page now emits Open Graph and Twitter card tags (title,
description, url, image) so links render branded previews on LinkedIn,
Slack, WhatsApp and X.

- Add a branded 1200x630 social card generated with next/og, served
  site-wide via app/opengraph-image and reused for app/twitter-image.
- Add buildMetadata() helper that produces matching title/description/
  canonical plus OG and Twitter card objects for each page.
- Route home and auth pages through the helper; add OG/Twitter defaults
  to the root layout for inheritance.

Refs #106
Footer links now resolve to real pages or anchors instead of placeholders
that fell back to the homepage, and the previously missing contact and
legal pages exist.

- Footer: grouped Product/Company/Legal navigation with a real contact
  email, on-page anchors (#how-it-works, #faq), the parent company site,
  and links to the new pages.
- Add /contact (indexable) with a real, monitored address.
- Add /privacy and /terms minimal legal pages (noindex,follow).
- Add SitePage shell for consistent standalone-page layout.

Refs #112
Serve a robots.txt that allows public pages, blocks the API and gated
admin/user areas, and points crawlers to the sitemap.

Refs #107
Serve a sitemap.xml listing the public, indexable pages with last-modified,
change frequency, and priority. The page list lives in a central
publicRoutes constant so adding a public page updates the sitemap from one
place; gated, admin, and API routes are excluded.

Refs #108
Emit JSON-LD on every page so search and AI answer engines can identify
the product, the organization behind it, and its offering.

- Site-wide @graph (Organization, WebSite, SoftwareApplication) rendered
  from the root layout.
- Organization mirrors Ajared coverage: legal name, two locations
  (Toronto, Abuja) with addresses and geo, contact point, and sameAs.
- SoftwareApplication describes Retailytics with category, offer (free
  trial), and a feature list.
- FAQPage emitted on the home page from the existing FAQ content.
- BreadcrumbList on standalone pages via SitePage.

Refs #109
@claude

claude Bot commented Jun 5, 2026

Copy link
Copy Markdown

Claude finished @prudentbird's task —— View job


Schema.org Structured Data Review

Review Tasks

  • Check repository guidelines (CLAUDE.md)
  • Review core structured data implementation
  • Examine JsonLd component
  • Analyze page integrations
  • Assess code quality and best practices
  • Check for potential bugs and security issues
  • Evaluate performance considerations
  • Review test coverage

🎯 Overall Assessment: HIGH QUALITY

This is a well-architected implementation of Schema.org structured data with excellent separation of concerns and proper cross-linking. The code follows modern React/Next.js patterns and provides comprehensive SEO coverage.


Strengths

Architecture & Design

  • Clean separation: structured-data.ts handles data logic, JsonLd handles rendering
  • Proper Schema.org implementation: Uses @graph for site-wide entities with correct cross-linking via @id
  • Comprehensive coverage: Organization, WebSite, SoftwareApplication, FAQPage, and BreadcrumbList schemas
  • Single source of truth: FAQ data reused between UI and structured data

Code Quality

  • TypeScript safety: Proper typing with ReadonlyArray and type definitions
  • Consistent patterns: All schema builders follow the same structure
  • Good naming: Clear, descriptive function and variable names
  • Proper imports: Clean dependency structure

SEO Implementation

  • Rich entity data: Two locations with PostalAddress + GeoCoordinates
  • Proper relationships: Publisher/provider cross-references using @id
  • Feature coverage: SoftwareApplication includes offers and feature list
  • Conditional rendering: Breadcrumbs only on relevant pages

⚠️ Security Concerns

Critical: Missing Input Sanitization

File: frontend/src/components/json-ld.tsx:5

dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}

Issue: Direct JSON serialization without sanitization could enable XSS if malicious data enters the system.

Risk: Medium - while current data sources appear safe, this creates a potential attack vector.

Recommendation: Add input validation or use a JSON sanitization library:

// Option 1: Validate input
if (typeof data !== 'object' || data === null) {
  throw new Error('Invalid structured data');
}

// Option 2: Use a sanitization library
import { serialize } from 'safe-json-stringify';
dangerouslySetInnerHTML={{ __html: serialize(data) }}

🐛 Potential Issues

1. Hardcoded Coordinates

File: frontend/src/lib/structured-data.ts:42,56

Coordinates are hardcoded for Toronto/Abuja. Consider:

  • Moving to siteConfig for maintainability
  • Adding validation for coordinate ranges
  • Documenting coordinate precision requirements

2. Missing Error Handling

File: frontend/src/lib/structured-data.ts:51

absoluteUrl() calls could fail with invalid paths. Consider adding validation.

3. Logo URL Dependency

File: frontend/src/lib/structured-data.ts:21

Uses /opengraph-image as logo. The PR notes this is temporary, but consider fallback handling.


🚀 Performance Considerations

Excellent Performance Characteristics

  • Minimal runtime cost: Static data generation
  • No client-side impact: Server-side rendering only
  • Efficient bundling: Tree-shakable exports
  • Cache-friendly: Static content ideal for CDN caching

Minor Optimizations

  • Consider memoizing schema builders if called frequently
  • FAQ data is small but could be lazy-loaded if it grows

🧪 Test Coverage: MISSING

Critical Gap: No tests for structured data functionality.

Recommended Test Coverage:

// Unit tests needed:
describe('structured-data', () => {
  test('organizationSchema generates valid Schema.org Organization')
  test('breadcrumbSchema handles empty/invalid inputs')  
  test('faqPageSchema properly formats FAQ data')
  test('siteGraph contains all required entities')
})

describe('JsonLd', () => {
  test('renders valid JSON-LD script tag')
  test('handles invalid data gracefully')
})

Integration Tests: Verify rendered HTML contains expected Schema.org markup.


📋 Best Practices Alignment

Follows Best Practices

  • ✅ Uses semantic Schema.org types
  • ✅ Implements proper entity relationships
  • ✅ Follows Next.js SSR patterns
  • ✅ TypeScript strict typing
  • ✅ Clean component composition

Could Improve

  • Add JSDoc comments for public APIs
  • Consider schema validation with a library like ajv
  • Add environment-specific configuration

🔧 Recommendations

High Priority

  1. Add input sanitization to JsonLd component
  2. Add comprehensive test coverage
  3. Move coordinates to configuration

Medium Priority

  1. Add error boundaries for schema generation
  2. Consider schema validation in development
  3. Add JSDoc documentation

Low Priority

  1. Add performance monitoring for large schemas
  2. Consider schema versioning strategy

🎉 This is a solid implementation that demonstrates good understanding of both SEO requirements and modern React patterns. The structured data will significantly improve search engine understanding of the application.

@prudentbird prudentbird changed the base branch from feat/seo-108-sitemap to dev June 5, 2026 19:49
@prudentbird prudentbird merged commit 42ba5df into dev Jun 5, 2026
1 check passed
@prudentbird prudentbird deleted the feat/seo-109-structured-data branch June 5, 2026 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(seo+geo): add Schema.org structured data

1 participant