Rendering Meta Tags with React Router Data APIs
React Router 7 (the evolution of Remix) ties metadata to its data layer: a loader fetches the route’s data on the server, and a meta export turns that data into title and meta tags rendered into the response. This keeps per-route metadata server-rendered and correct for crawlers, instead of being injected client-side after load. It is the data-API approach within React Router SEO best practices.
Step-by-step fix
-
Load the route’s data in a
loader. It runs on the server before rendering.// app/routes/products.$id.jsx export async function loader({ params }) { const product = await getProduct(params.id); return { product }; } -
Return tags from the
metafunction using loader data.// ✅ Server-rendered, derived from loader data export function meta({ data }) { return [ { title: `${data.product.name} — Example` }, { name: 'description', content: data.product.summary }, { property: 'og:title', content: data.product.name }, { tagName: 'link', rel: 'canonical', href: `https://example.com/products/${data.product.id}` }, ]; } -
Avoid client-only meta injection for SEO-critical routes.
// ❌ Runs only after hydration; crawlers may miss it useEffect(() => { document.title = product.name; }, [product]); -
Confirm server rendering is enabled so the meta export actually runs on the server and ships in the response.
Validation
curlof the route shows the title and meta tags in the HTML response.- View source (not just rendered DOM) contains the tags.
- GSC URL Inspection rendered HTML matches the live head.
- Hydration produces no mismatch warning for the head.
Reference
// app/routes/blog.$slug.jsx — loader + meta, server-rendered
export async function loader({ params }) {
const post = await getPost(params.slug);
if (!post) throw new Response('Not Found', { status: 404 });
return { post };
}
export function meta({ data }) {
if (!data?.post) return [{ title: 'Not Found' }];
return [
{ title: data.post.title },
{ name: 'description', content: data.post.summary },
{ property: 'og:image', content: data.post.ogImage },
{ tagName: 'link', rel: 'canonical', href: `https://example.com/blog/${data.post.slug}` },
];
}
Frequently Asked Questions
Does the React Router meta export render on the server? In framework mode (React Router 7 / Remix) with server rendering enabled, the meta export runs on the server and its tags are in the initial HTML response. In a client-only SPA configuration, you need server rendering or prerendering for the tags to reach crawlers reliably.
How do I use loader data in the meta function? The meta function receives the route’s loader data as an argument, so you can return a title and description derived from it. Because the loader runs before rendering, the data is available when the meta tags are produced.
Related
- React Router SEO Best Practices — the full React Router SEO guide.
- How to fix React hydration mismatch SEO warnings — keep server and client meta aligned.
- Updating document title in React without Helmet — the client-side fallback approach.
← Back to React Router SEO Best Practices