SEO, assets, and styling
Own route metadata, optimized media, fonts, scripts, and component styling without losing SSR safety.
Put metadata next to the route
Use a page head export when metadata belongs to one route. Use Seo for structured title, description, canonical URL, robots, Open Graph, Twitter, and theme-color values. Dynamic head functions can read route params and loader data.
tsximport { Seo } from "@tavojs/core";
export const head = <Seo
title="Project dashboard"
description="Track active projects and delivery status."
canonical="https://example.com/dashboard"
openGraph={{ type: "website", image: "https://example.com/og/dashboard.png" }}
/>;Use framework asset components
Image provides one API for CSR assets and optional SSR optimization. Optimized remote images require an explicit HTTPS host allowlist; local assets must remain inside the public directory.
Keep styling boundaries clear
Use CSS modules or Tavo.js UI component props for ordinary local presentation. The lower-level style registry is for libraries that must register deduplicated CSS during both SSR and browser rendering.
Call style(id, css) while an SSR registry is active; repeated IDs are emitted once.
Use
ensureClientStyle(id, css) when a browser-only integration must install one managed style element.Keep IDs stable across server and browser rendering so hydration does not duplicate CSS.
tsximport {
createStyleRegistry,
renderStyleTags,
renderToString,
style,
withStyleRegistry,
} from "@tavojs/core";
function StatusBadge({ label }: { label: string }) {
style(
"status-badge",
".status-badge{border-radius:999px;padding:.25rem .5rem}"
);
return <span className="status-badge">{label}</span>;
}
const registry = createStyleRegistry();
const body = withStyleRegistry(registry, () => {
return renderToString(<StatusBadge label="Ready" />);
});
const styles = renderStyleTags(registry);
const html = `${styles}${body}`;Head and SEO properties
Use the route head export for route-owned metadata and
Headfor component-owned insertion. Raw HTML is accepted only throughunsafeHeadHtml; there is no head string alias.An explicit robots string takes precedence over
noIndexandnoFollow. Arrays of keywords become a comma-separated meta value.SEO title, description, canonical, and Open Graph image provide fallbacks for corresponding Open Graph and Twitter fields.
unsafeHeadHtmlis not escaped. Never place user-controlled data in it.
tstype PageHead = {
title?: string;
unsafeHeadHtml?: string;
status?: number;
htmlAttributes?: Record<string, string | number | boolean>;
bodyAttributes?: Record<string, string | number | boolean>;
};
type HeadProps = {
title?: string;
unsafeHeadHtml?: string;
children?: Child;
};
type SeoProps = {
title?: string; description?: string; canonical?: string;
robots?: string; noIndex?: boolean; noFollow?: boolean;
keywords?: string | string[]; author?: string; themeColor?: string;
openGraph?: SeoOpenGraph; twitter?: SeoTwitter;
};Image properties and optimizer defaults
Default candidate widths are 320, 640, 960, 1280, and 1600. A supplied width also adds its 2x candidate; values above 3840 or at most zero are removed.
Quality defaults to 75 and is rounded and clamped from 1 to 100. Format defaults to webp and generated sizes defaults to 100vw.
priority selects eager loading and high fetch priority. Other images default to lazy loading; decoding defaults to async.
Set unoptimized to keep the original URL. Remote optimization requires an HTTPS allowlist; local sources must stay inside
publicDir.
tsx<Image
src="/images/hero.jpg"
alt="Team reviewing a release"
width={1280}
height={720}
widths={[640, 960, 1280]}
sizes="(max-width: 48rem) 100vw, 60vw"
quality={80}
format="webp"
priority
/>Font and Script properties
Fontuses href for an external stylesheet or src plus family for a self-hosted @font-face. Self-hosted fonts preload by default; external stylesheet preload is opt-in.Self-hosted format is inferred from woff2, woff, ttf, or otf when type is omitted.
crossOrigindefaults to anonymous for font preloads.Fontvariable must be a CSS custom property name. fallback is appended to the generated variable value.Scriptsupports src, type, async, defer, module,noModule, preload, content, json, id, nonce, integrity,crossOrigin,referrerPolicy, andfetchPriority.A json value without src defaults to application/ld+json. Inline content escapes less-than signs and closing script sequences.
tsx<Font
src="/fonts/brand.woff2"
family="Brand Sans"
display="swap"
variable="--font-brand"
/>
<Script
json={{ "@context": "https://schema.org", "@type": "WebSite", name: "Acme" }}
nonce={nonce}
/>
<Script src="https://cdn.example.com/widget.js" defer integrity={integrity} />Look up exact public types
Follow linked API names to their canonical TypeScript declarations and package boundaries.