Next.js in these templates
The App Router conventions every template follows, and why so little of it is a client component.
UT Studio7 min read
Every template in the catalogue is built on the Next.js App Router with React Server Components as the default. This page describes the conventions they share, so that the second template you open needs no re-learning.
Server by default, client at the edges
A component is a Server Component unless it needs state, an effect or a browser API. In practice that means the vast majority of a template — every heading, list, table, card and layout — never reaches the browser as JavaScript at all.
The parts that do are small and specific: a theme switcher, a cart, a mobile menu. Each is an island with its own `use client` boundary, and each takes its data as props rather than importing the content layer, so choosing a licence tier does not ship the catalogue to the browser.
State that belongs in the URL
Filtering, sorting, search and pagination are query parameters read on the server. That is a design decision with four consequences worth stating, because they are the reason it is worth the discipline:
- A filtered view is shareable. Someone can send a colleague exactly what they are looking at.
- The back button works, because every filter change is a navigation.
- Reload restores the view instead of resetting it.
- The cost of interacting does not grow with the size of the catalogue, because nothing re-renders on the client.
export default async function Page({
searchParams,
}: {
searchParams: Promise<Record<string, string | string[] | undefined>>;
}) {
const filters = parseFilters(await searchParams);
const result = queryCatalog(filters);
return <Grid products={result.products} />;
}Rendering strategies
| Strategy | Used for | Why |
|---|---|---|
| Static | Marketing pages, docs, articles | Content ships with the deploy; nothing to compute per request |
| SSG with params | Product detail pages | Known set of routes, prerendered at build |
| Dynamic | Anything reading search params or a session | The output genuinely differs per request |
A page that reads searchParams is dynamic
That is correct, not a mistake — it must be, to answer with the right filter. Prerendering the unfiltered view of such a page is a caching decision, not a rendering one.
Metadata
Every route exports metadata derived from its content rather than written twice. Titles, descriptions, canonicals and OpenGraph images all come from one builder, so a page cannot end up with a canonical that disagrees with its own URL.
Templates with the most application surface
Templates mentioned here
Everything above is written against real products. These are the ones this page draws on.


