On this page
· 2 min read
Layouts & Rendering
Handlebars turns each request into a complete HTML document at SSR time. The browser receives static HTML plus optional scripts (client.js, Pagefind, inline layout scripts).
The SSR entry contract
pagesmithSsg loads src/entry-server.tsx and uses:
export async function getRoutes(config: SsgRenderConfig): Promise<string[]>;export async function render(url: string, config: SsgRenderConfig): Promise<string>;getRoutes() returns every path to generate. render(url, config) returns one HTML string per path. config includes base, root, asset paths, and flags such as searchEnabled.
Partials vs one-time setup
Helpers (formatDate, eq, startsWith, or, concat) are registered once when the entry module loads.
The layout partial is different: Handlebars.registerPartial('layout', loadTemplate(root, 'layout')) runs at the start of each render() call. That is deliberate so edits to templates/layout.hbs are picked up on the next dev request without restarting Vite. Page templates (article, index, about) are recompiled each render for the same reason.
The content layer is cached separately (getLayer keeps a single createContentLayer instance per root) so markdown is not re-read from disk on every partial tweak unless content watchers invalidate it through normal dev reload behavior.
Template composition
Page templates use the layout as a partial and supply a body inline partial:
{{#> layout}} {{#*inline "body"}} <article id="doc-main-content" tabindex="-1" data-pagefind-body> ... <div class="prose"> {{{content}}} </div> </article> {{/inline}}{{/layout}}Inside layout.hbs, {{> body}} is where that inline partial is injected. Shared chrome (header, sidebar shell, Pagefind modal) lives in the layout once.
Data and escaping
{{title}}— HTML-escaped text.{{{content}}}— Raw HTML fromentry.render()(trusted markdown output).
Nested {{#each}} blocks change Handlebars scope; parent fields need ../ (see the sidebar links in templates/layout.hbs).
End-to-end page build
- SSG calls
render('/guide/installation', config). - Entry normalizes the URL (strips
config.base), loads collections via the content layer, finds the guide entry. - Compiled
article.hbsruns with template data; the layout partial fills the shell. - Plugin writes
guide/installation/index.htmlunder the configuredoutDir.
The same pipeline applies to guide/kitchen-sink, the home route (index.hbs), and /about (about.hbs).