Server Metadata

Set page titles, meta tags, Open Graph, and lang attributes with the metadata() function.

The metadata() function lets you define SEO and head tags for each page, running on the server before load().

Basic Usage

Export a metadata function from +page.server.ts:

import type { MetadataEvent } from "bosia";

export function metadata({ params }: MetadataEvent) {
	return {
		title: "About — My App",
		description: "Learn more about our app.",
	};
}

This renders a <title> tag and a <meta name="description"> tag in the page <head>.

Open Graph & Social Tags

Use the meta array to add Open Graph, Twitter Card, or any custom meta tags:

export function metadata({ params }: MetadataEvent) {
	return {
		title: "Blog Post",
		description: "A great blog post.",
		meta: [
			{ property: "og:title", content: "Blog Post" },
			{ property: "og:description", content: "A great blog post." },
			{ property: "og:type", content: "article" },
			{ name: "twitter:card", content: "summary_large_image" },
		],
	};
}

Tags with property render as <meta property="...">, tags with name render as <meta name="...">.

Set the <html lang> attribute and add <link> tags for canonical URLs, hreflang alternates, and more:

export function metadata() {
	return {
		title: "Mon Blog",
		lang: "fr",
		link: [
			{ rel: "canonical", href: "https://example.com/blog" },
			{ rel: "alternate", href: "https://example.com/en/blog", hreflang: "en" },
			{ rel: "alternate", href: "https://example.com/fr/blog", hreflang: "fr" },
		],
	};
}

Passing Data to load()

The data property lets you share fetched data with load(), avoiding duplicate queries:

import type { MetadataEvent, LoadEvent } from "bosia";

export function metadata({ params }: MetadataEvent) {
	const post = await db.getPost(params.slug);
	return {
		title: `${post.title} — Blog`,
		description: post.excerpt,
		meta: [{ property: "og:title", content: post.title }],
		// Pass to load() — avoids a second DB query
		data: { post },
	};
}

export async function load({ params, metadata }: LoadEvent) {
	// Reuse data from metadata(), fall back to fresh query
	const post = metadata?.post ?? (await db.getPost(params.slug));
	return { post };
}

The data object from metadata() becomes event.metadata in load(). If no metadata() function exists, event.metadata is null.

MetadataEvent Properties

Property Type Description
params Record<string, string> Dynamic route parameters
url URL The request URL
locals Record<string, any> Data set by middleware hooks
cookies Cookies Read/write cookies
fetch Function Fetch helper (cookies forwarded same-origin only — see Server Loaders → Cookie Forwarding)

Metadata Return Type

Property Type Description
title string Page <title> tag
description string <meta name="description"> tag
meta Array<{ name?: string; property?: string; content: string }> Custom meta tags
lang string <html lang> attribute
link Array<{ rel: string; href: string; hreflang?: string }> <link> tags (canonical, hreflang, etc.)
data Record<string, any> Data passed to load() as event.metadata

All properties are optional.

Client-Side Navigation

During client-side navigation, Bosia sends title and description from metadata() in the data response. The client router automatically updates the document title and description meta tag without a full page reload.

Timeouts

The metadata() function has a configurable timeout via the METADATA_TIMEOUT environment variable (in milliseconds). If metadata() takes too long, it times out gracefully and the page renders without metadata.

Caching Interaction

If metadata() calls cookies.get() or cookies.getAll(), the data response is automatically marked with Cache-Control: private, no-cache. See Server Loaders for details.