Generate Metadata
TanStack Start

Functions

Complete reference for all generate-metadata functions in TanStack Start

A simple guide to using generate-metadata functions in your TanStack Start application.

Quick Start

First, create a metadata client:

lib/metadata.ts
import { GenerateMetadataClient } from "generate-metadata/tanstack-start";
 
export const metadataClient = new GenerateMetadataClient({
  dsn: process.env.GENERATE_METADATA_DSN,
});

getHead()

Generate AI-powered metadata for individual routes.

Basic Usage

routes/index.tsx
import { metadataClient } from "@/lib/metadata";
 
export const head = metadataClient.getHead(() => ({
  path: "/",
}));
 
export default function HomePage() {
  return <h1>Welcome to my site!</h1>;
}

Dynamic Routes

For routes with dynamic parameters, include them in the path:

routes/blog.$slug.tsx
import { getRouteApi } from "@tanstack/react-start";
 
const routeApi = getRouteApi("/blog/$slug");
 
export const head = metadataClient.getHead(() => {
  const { slug } = routeApi.useParams();
 
  return {
    path: `/blog/${slug}`,
  };
});
 
export default function BlogPost() {
  const { slug } = routeApi.useParams();
  return <h1>Blog Post: {slug}</h1>;
}

With Search Parameters

Include search parameters in the path for better caching:

routes/search.tsx
import { getRouteApi } from "@tanstack/react-start";
 
const routeApi = getRouteApi("/search");
 
export const head = metadataClient.getHead(() => {
  const searchParams = routeApi.useSearch();
  const query = searchParams.q || "";
 
  return {
    path: `/search?q=${query}`,
  };
});

getRootHead()

Generate metadata for layout components.

Simple Layout

routes/__root.tsx
import { metadataClient } from "@/lib/metadata";
 
// No configuration needed for basic layout
export const head = metadataClient.getRootHead();
 
export default function RootLayout({ children }) {
  return (
    <html>
      <body>{children}</body>
    </html>
  );
}

Layout with Fallback

If you want default metadata when the API is unavailable:

routes/__root.tsx
export const head = metadataClient.getRootHead(() => ({
  fallback: {
    meta: [
      { name: "title", content: "My Amazing App" },
      { name: "description", content: "The best app you'll ever use" },
    ],
  },
}));

Fallback Metadata

Use fallback metadata when the API is unavailable. It's optional but recommended:

routes/products.tsx
export const head = metadataClient.getHead(() => ({
  path: "/products",
  fallback: {
    meta: [
      { name: "title", content: "Our Products" },
      { name: "description", content: "Browse our amazing collection" },
    ],
  },
}));

Override Metadata

Use override to force certain metadata values, regardless of what the API returns:

routes/index.tsx
export const head = metadataClient.getHead(() => ({
  path: "/",
  fallback: {
    meta: [
      { name: "title", content: "Home Page" },
      { name: "description", content: "Welcome to our website" },
    ],
  },
  override: {
    meta: [
      { name: "robots", content: "index,follow" },
      { name: "viewport", content: "width=device-width, initial-scale=1" },
    ],
  },
}));

Complete Example

Here's a complete example showing all features:

routes/products.$id.tsx
import { metadataClient } from "@/lib/metadata";
import { getRouteApi } from "@tanstack/react-start";
 
const routeApi = getRouteApi("/products/$id");
 
export const head = metadataClient.getHead(() => {
  const { id } = routeApi.useParams();
 
  return {
    path: `/products/${id}`,
    fallback: {
      meta: [
        { name: "title", content: "Product Details" },
        { name: "description", content: "View detailed product information" },
      ],
    },
    override: {
      meta: [{ name: "robots", content: "index,follow" }],
    },
  };
});
 
export default function ProductPage() {
  const { id } = routeApi.useParams();
  return <h1>Product ID: {id}</h1>;
}

Common Patterns

E-commerce Product Pages

routes/products.$id.tsx
export const head = metadataClient.getHead(() => {
  const { id } = routeApi.useParams();
 
  return {
    path: `/products/${id}`,
    fallback: {
      meta: [
        { name: "title", content: "Product - My Store" },
        {
          name: "description",
          content: "High-quality products at great prices",
        },
      ],
    },
  };
});

Blog Posts

routes/blog.$slug.tsx
export const head = metadataClient.getHead(() => {
  const { slug } = routeApi.useParams();
 
  return {
    path: `/blog/${slug}`,
    fallback: {
      meta: [
        { name: "title", content: "Blog Post - My Blog" },
        {
          name: "description",
          content: "Read our latest thoughts and insights",
        },
      ],
    },
  };
});

Category Pages

routes/category.$name.tsx
export const head = metadataClient.getHead(() => {
  const { name } = routeApi.useParams();
 
  return {
    path: `/category/${name}`,
    fallback: {
      meta: [
        { name: "title", content: `${name} Category` },
        {
          name: "description",
          content: `Browse all items in the ${name} category`,
        },
      ],
    },
  };
});

revalidateHandler()

Creates a Hono app instance for handling cache revalidation requests. This method allows you to programmatically clear cached metadata when your content changes.

Note: Unlike the Next.js adapter which returns Next.js-compatible route handlers, this method returns a Hono app instance that you can integrate with your TanStack Start application.

Basic Usage

app/api/generate-metadata/revalidate.ts
import { metadataClient } from "@/lib/metadata";
 
export const app = metadataClient.revalidateHandler({
  revalidateSecret: process.env.GENERATE_METADATA_REVALIDATE_SECRET!,
});
 
// You can then mount this Hono app in your TanStack Start application

Custom Revalidation Logic

You can provide a custom revalidatePath function to handle revalidation according to your needs:

app/api/generate-metadata/revalidate.ts
import { metadataClient } from "@/lib/metadata";
 
export const app = metadataClient.revalidateHandler({
  revalidateSecret: process.env.GENERATE_METADATA_REVALIDATE_SECRET!,
  revalidatePath: async (path: string | null) => {
    if (path !== null) {
      // Clear specific path in your custom cache
      await myCustomCache.invalidate(path);
      // Trigger CDN purge
      await myCDN.purge(path);
    } else {
      // Clear all paths
      await myCustomCache.clear();
      await myCDN.purgeAll();
    }
  },
});

API Endpoint

The handler creates a POST /revalidate endpoint that accepts:

{
  "path": "/some-path" // or null to clear all paths
}

Authentication

The endpoint supports two authentication methods:

  1. Bearer Token: Include Authorization: Bearer <secret> header
  2. HMAC Signature: For webhook integrations (see webhook verification docs)

Response

On success:

{
  "success": true,
  "revalidated": true,
  "path": "/some-path"
}

That's it! Your TanStack Start app now has AI-powered metadata generation.