[]previous and next page

The exports

A module exports metadata or generateMetadata, not both.

For agentsUpdated
Export Where What it does
metadata route, layout A Metadata object (static)
generateMetadata route, layout (args) => Metadata | Promise<Metadata>; receives params, search, parent and loaderData
viewport route, layout A Viewport: the viewport meta, theme color and color scheme
generateViewport route, layout (args) => Viewport | Promise<Viewport>
links route, layout Array of link descriptors ({ rel, href, as, ... }), or a function returning one
meta route, layout React Router style descriptors ({ title }, { name, content }, { property, content }, { tagName: "link", ... })
headers route, layout Response headers: an object, or (args) => object with parentHeaders

A module exports metadata or generateMetadata, not both. The same holds for viewport and generateViewport; a module that does both reports a TypeError and its layer contributes nothing. The head export of Routing still works, and the metadata tags win over it for the same tag.

src/routes/_layout.tsxtsx
import type { Metadata, Viewport } from "@shadow-js/dusk";
import { Outlet } from "@shadow-js/router";
import type { JSX } from "@shadow-js/web";

export const metadata: Metadata = {
  metadataBase: "https://example.com",
  title: { default: "Example", template: "%s · Example" },
  description: "Storage for agent plans, files and sites.",
  alternates: { canonical: "./" },
  openGraph: { type: "website", siteName: "Example", images: ["/og.png"] },
  robots: { index: true, follow: true, googleBot: { "max-image-preview": "large" } },
};

export const viewport: Viewport = {
  width: "device-width",
  initialScale: 1,
  themeColor: "#0b0b0a",
  colorScheme: "dark",
};

export default function Layout(): JSX.Element {
  return (
    <div>
      <Outlet />
    </div>
  );
}
src/routes/posts/[id].tsxtsx
import type { MetadataArgs } from "@shadow-js/dusk";
import type { JSX } from "@shadow-js/web";

type Post = { readonly title: string; readonly body: string };

export const generateMetadata = ({ loaderData, params }: MetadataArgs<{ id: string }, Post>) => ({
  title: loaderData?.title ?? `Post ${params.id}`,
  description: loaderData?.body.slice(0, 150),
  openGraph: { type: "article" as const, publishedTime: "2026-10-09T00:00:00Z" },
});

export default function Post(): JSX.Element {
  return <article>Post</article>;
}