Page Metadata
Retend's router supports page metadata such as titles, descriptions, Open Graph tags, and Twitter Cards. Metadata is defined per route and updates when navigation occurs. With SSR or SSG, the metadata is included in the initial HTML head.
Setting a Page Title
Set the page title with the title property on a route definition:
const routes = defineRoutes([ { path: '/', component: Home, title: 'Home | My App', }, { path: '/about', component: About, title: 'About Us | My App', }, ]);
When navigation reaches /about, the browser tab title becomes "About Us | My App".
Route Metadata
For descriptions and social sharing tags, use the metadata property:
const routes = defineRoutes([ { path: '/', component: Home, metadata: { title: 'Home | My App', description: 'Welcome to My App.', ogTitle: 'My App', ogImage: 'https://example.com/og-image.png', }, }, ]);
Available Properties
Retend recognizes these metadata keys and maps them to the appropriate HTML tags:
| Key | HTML Output |
|---|---|
title | <title> |
description | <meta name="description"> |
author | <meta name="author"> |
keywords | <meta name="keywords"> |
viewport | <meta name="viewport"> |
lang | <html lang="..."> |
charset | <meta charset="..."> |
themeColor | <meta name="theme-color"> |
Open Graph (for Facebook, LinkedIn, etc.):
| Key | HTML Output |
|---|---|
ogTitle | <meta property="og:title"> |
ogDescription | <meta property="og:description"> |
ogImage | <meta property="og:image"> |
ogUrl | <meta property="og:url"> |
ogType | <meta property="og:type"> |
ogLocale | <meta property="og:locale"> |
ogSiteName | <meta property="og:site_name"> |
Twitter Cards:
| Key | HTML Output |
|---|---|
twitterCard | <meta name="twitter:card"> |
twitterTitle | <meta name="twitter:title"> |
twitterDescription | <meta name="twitter:description"> |
twitterImage | <meta name="twitter:image"> |
Dynamic Metadata
To derive metadata from the current URL, pass a function instead of an object. For example, a user profile page can use the user's name in its title:
const routes = defineRoutes([ { path: '/users/:userId', component: UserProfile, metadata: async ({ params }) => { const userId = params.get('userId'); const user = await fetchUser(userId); return { title: `${user.name} | My App`, description: user.bio, ogTitle: user.name, ogImage: user.avatar, }; }, }, ]);
The function receives an object with:
params: AMap<string, string>of matched URL parameters.query: AURLSearchParamsof the query string.
Component-Level Metadata
Metadata can also be attached directly to a component function:
function BlogPost(props) { return <article>...</article>; } BlogPost.metadata = async ({ params }) => { const post = await fetchPost(params.get('slug')); return { title: post.title, description: post.excerpt, }; };
This places metadata with the component that uses it. If both the route and component define metadata, the values are merged and component metadata takes precedence.
Metadata Cascading
In nested routes, metadata accumulates from parent to child. Child routes override parent values for the same key:
const routes = defineRoutes([ { path: '/', component: Layout, metadata: { ogSiteName: 'My App', lang: 'en', }, children: [ { path: 'blog', component: BlogList, metadata: { title: 'Blog | My App', description: 'Read our latest posts.', }, }, ], }, ]);
When the user visits /blog, the final metadata combines both levels:
ogSiteName: "My App" (from parent)lang: "en" (from parent)title: "Blog | My App" (from child)description: "Read our latest posts." (from child)