<head> Management
τjs provides flexible document <head> management through a headContent function defined in your entry-server.tsx.
Composition Order
Section titled “Composition Order”τjs assembles the document head in a fixed, intentional order:
-
Template (
index.html)- Provides the static baseline:
<html>,<head>,<body><meta charset>,<meta viewport>- global links and static tags
- Acts as the structural shell of the document.
- Provides the static baseline:
-
Route Meta (
taujs.config.ts)- Declares intent at the routing layer.
- Used for SEO-critical, deterministic values:
titledescription- Open Graph defaults
- Always available, including in streaming routes.
-
Renderer Head (
headContentinentry-server.tsx)- Converts
metaand (optionally)datainto actual HTML tags. - Can enrich or override route meta.
- Runs:
- after data resolution in SSR
- at shell-ready time in streaming
- Converts
This separation ensures:
- routing controls what the page represents
- rendering controls how it becomes HTML
- streaming remains safe for SEO
The headContent Function
Section titled “The headContent Function”import { createRenderer } from "@taujs/react";import { App } from "./App";
export const { renderSSR, renderStream } = createRenderer({ appComponent: ({ location }) => <App location={location} />, headContent: ({ data, meta }) => ` <title>${meta?.title || "τjs - Composing systems, not just apps"}</title> <meta name="description" content="${ meta?.description || data?.message || "τjs - Composing systems, not just apps" }"> `,});Data Sources
Section titled “Data Sources”Result from your route’s attr.data handler:
// Route config{ path: '/products/:id', attr: { render: 'ssr', data: async (params) => { const product = await db.products.findById(params.id); return { title: product.name, description: product.description, image: product.imageUrl }; } }}From your route’s attr.meta configuration:
// Route config{ path: '/about', attr: { render: 'ssr', meta: { title: 'About Us', description: 'Learn about our company' } }}Data Availability by Mode
Section titled “Data Availability by Mode”SSR Mode
Section titled “SSR Mode”Data is always fully resolved before headContent runs:
headContent: ({ data, meta }) => ` <title>${meta?.title || "Products"}</title> <meta name="description" content="${data.product.description}">`;Streaming Mode
Section titled “Streaming Mode”Data may not be ready when headContent runs. Use meta for reliable SEO:
headContent: ({ data, meta }) => { // Use meta for guaranteed values return ` <title>${meta?.title || "Streaming page"}</title> <meta name="description" content="${meta.description}"> ${ data.ogImage ? `<meta property="og:image" content="${data.ogImage}">` : "" } `;};Because
headContentruns before all data is available in streaming mode, SEO-critical values should come from routemeta, not fetched data.
Common Patterns
Section titled “Common Patterns”Open Graph Tags
Section titled “Open Graph Tags”headContent: ({ data, meta }) => { const title = data?.title || meta?.title || "Default title"; const description = data?.description || meta?.description || ""; const image = data?.ogImage || meta?.ogImage;
return ` <title>${title}</title> <meta property="og:title" content="${title}"> <meta property="og:description" content="${description}"> ${image ? `<meta property="og:image" content="${image}">` : ""} `;};Structured Data (JSON-LD)
Section titled “Structured Data (JSON-LD)”headContent: ({ data, meta }) => { const jsonLd = data?.jsonLd || meta?.jsonLd;
return ` <title>${meta?.title || "Page"}</title> ${ jsonLd ? `<script type="application/ld+json">${JSON.stringify( jsonLd )}</script>` : "" } `;};If you use CSP with script-src restrictions, inline JSON-LD may require a nonce/hash depending on your policy.
Canonical URLs
Section titled “Canonical URLs”headContent: ({ data, meta }) => { const canonical = data.canonical || meta.canonical;
return ` <title>${meta.title}</title> ${canonical ? `<link rel="canonical" href="${canonical}">` : ""} `;};Best Practices
Section titled “Best Practices”1. Prioritise Meta Over Data in Streaming
Section titled “1. Prioritise Meta Over Data in Streaming”// reliable in streamingheadContent: ({ data, meta }) => ` <title>${meta?.title || "Default Title"}</title> <meta name="description" content="${ meta?.description || "Default description" }"> ${data?.ogImage ? `<meta property="og:image" content="${data.ogImage}">` : ""}`;2. Provide Fallbacks
Section titled “2. Provide Fallbacks”const title = data.title || meta.title || "Default Title";3. Escape User Content
Section titled “3. Escape User Content”function escapeHtml(str: string): string { return str .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """);}4. Use SSR for Data-Dependent Head
Section titled “4. Use SSR for Data-Dependent Head”If your head content critically depends on fetched data, use render: 'ssr'.