SEO Utilities

SEO Utilities

Two helpers in src/lib/seo.ts centralize meta tags and structured data so every public page emits consistent SEO markup without hand-rolling tag arrays.

getSeoMeta(params?)

Returns a TanStack Router head.meta array: title, description, Open Graph (og:title, og:description, og:url, og:type, optional og:image), and Twitter card tags. Every field falls back to config.seo defaults (defaultTitle, defaultDescription, defaultOgImage, twitterHandle) and config.domainName, so a bare getSeoMeta() is always valid.

import { getSeoMeta } from '@/lib/seo';

export const Route = createFileRoute('/_landing/pricing')({
  head: () => ({
    meta: getSeoMeta({
      title: 'Pricing',
      description: 'Simple plans that scale with you.',
      url: `https://${config.domainName}/pricing`
    })
  }),
  component: PricingPage
});

Params: title, description, url, image, type ('website' | 'article') , all optional. og:image/twitter:image and twitter:site are emitted only when a value exists, so unset config never produces empty tags.

getJsonLd(params)

Returns a schema.org JSON-LD object for Article, WebSite, Organization, or SoftwareApplication. Article adds datePublished + author (as a Person); SoftwareApplication adds the category/OS/offer boilerplate Google expects for app listings. Render it into a script tag in the route head:

scripts: [
  {
    type: 'application/ld+json',
    children: JSON.stringify(
      getJsonLd({ type: 'Article', name: post.title, datePublished: post.date })
    )
  }
];

Where it's used

All _landing.* routes (index, pricing, features, blog index + $slug, tos, privacy, changelog) and the compare/* pages. Blog post routes are the reference for the Article JSON-LD + type: 'article' meta combination (src/routes/_landing.blog.$slug.tsx).

Adding SEO to a new public page

  1. Add head: () => ({ meta: getSeoMeta({ ... }) }) to the route definition.
  2. Pass an absolute url (prefix with https://${config.domainName}).
  3. Only pages that should rank need JSON-LD; skip it for utility pages.
  4. Defaults live in config.ts under seo: , change branding there, not per page.