[]previous and next page

Templates

Templates in the Routing and data guide.

For agentsUpdated

_template.tsx is the template of its folder. It sits in the tree where a layout does: it renders <Outlet /> where the matched child goes, and it wraps the pages of its folder and the folders below. The difference is what happens on a navigation. A layout is built once and keeps its state while the user moves between its children. A template is built again, so its state starts over:

_layout.tsx _template.tsx
Built Once per route; kept while the route stays on screen Again whenever the pathname changes
State created inside it (signals, effects, onMount) Survives navigations between its children Starts from the first value on every navigation; the previous instance is disposed
Search or hash change Kept Kept: only the pathname counts
Needs a layout in the folder n/a No. With a layout in the same folder the template sits inside it (layout, then template, then page); without one it wraps the folder's pages directly

When a folder has both, the order is layout, then template, then the page. Nested templates compose from the outside in, and every template on the page is keyed by the same thing, the matched pathname, so a navigation that changes the pathname rebuilds all of them, together with everything under them (the page and any nested layout, whose state starts over too). Use a template for what has to restart per page: an enter animation, a per-page counter or form draft, an effect that should run again (a page-view ping in onMount).

Saved as src/routes/posts/_template.tsx:

posts-template.tsxtsx
import { useSignal } from "@shadow-js/core";
import { Outlet } from "@shadow-js/router";
import type { JSX } from "@shadow-js/web";

export default function PostsTemplate(): JSX.Element {
  // Created again on every navigation inside /posts, so each page starts with a count of 0.
  const [clicks, setClicks] = useSignal(0);
  return (
    <section class="posts-enter">
      <button type="button" onClick={() => setClicks(clicks() + 1)}>
        Clicked {clicks()} times on this page
      </button>
      <Outlet />
    </section>
  );
}

What to know:

  • The key is the whole pathname, so the rule is broad: going from /posts/1 to /posts/2 (the same page with a new param) rebuilds the template, and so does going from /posts/1/comments to /posts/1/likes, a change two levels below it. Next.js describes its templates as remounting when the segment below them changes; do not rely on this router remounting in exactly the same cases as Next.js does.
  • A rebuild does not refetch anything: loader data lives in the router's cache, outside the template.
  • The server renders the template once and the browser hydrates the same markup; the first render on the client does not rebuild it.
  • A template needs a default export (ROUTE_NO_EXPORTS otherwise) and is not an API route. A _template.tsx with no pages under it is not a route and is ignored.
  • The template's id is its folder pattern plus /_template (/posts/_template), and routeMeta lists it with the kind layout, since it is a pathless route that wraps its children.
  • An app without a _template.tsx is unchanged: the generated route tree is the same text as before and nothing about templates is imported. In an app with one, the tree wraps each template component with defineTemplate from @shadow-js/router, the function that does the remount.