skip to content

In AnalogJS, SSR is on by default; how would you prerender the blog, keep the catalogue server-rendered and make an admin area client-only?

level: seniorimportance: should knowfreq 23%

answer

  1. one analog() call in vite.config.ts
  2. the default already renders per request
  3. a routes list, possibly async
  4. Nitro routeRules by URL pattern
  5. static drops the server entirely

basics

~10 s

In AnalogJS, list the blog URLs in prerender.routes, leave the catalogue to the default ssr: true, and set nitro.routeRules { '/admin/**': { ssr: false } } so admin pages render only in the browser.

solid answer

~40 s

`analog()` in `vite.config.ts` defaults `ssr` to `true`, so the catalogue already renders on each request and its `.server.ts` `load` runs per request. The blog goes in `prerender.routes`: an array, or an async function returning strings, `{ route, staticData }` objects, and `{ contentDir, transform }` objects that turn Markdown files into URLs. Analog prerenders `/` by default only when you give no routes, so include `'/'` in your own list. For the admin area, `nitro: { routeRules: { '/admin/**': { ssr: false } } }` makes those URLs serve the client-only shell. `static: true` is the other extreme: prerender the listed pages and build no server, which breaks the per-request catalogue, every API route, and client navigations to pages with a `load` unless their data was prerendered with `staticData: true`.

code

ts · 28 lines
ts
// vite.config.ts
import { defineConfig } from 'vite';
import analog, { type PrerenderContentFile } from '@analogjs/platform';

export default defineConfig(() => ({
  plugins: [
    analog({
      // ssr: true is the default, so /catalogue renders per request
      prerender: {
        routes: async () => [
          '/', // keep it: supplying routes replaces the default
          '/blog',
          {
            contentDir: 'src/content/blog',
            transform: (file: PrerenderContentFile) =>
              file.attributes['draft'] ? false : `/blog/${file.attributes['slug'] || file.name}`,
          },
        ],
        sitemap: { host: 'https://www.example.com' },
      },
      nitro: {
        routeRules: {
          '/admin/**': { ssr: false },
        },
      },
    }),
  ],
}));

go deeper

for a junior

Recall that Analog renders on the server by default and that prerender.routes in vite.config.ts lists pages built ahead of time.

for a middle

Explain the three outcomes: per-request SSR, prerendered routes (strings, route configs, content directories), and client-only routes through nitro.routeRules with ssr: false.

for a senior

Show the trade-offs: supplying routes drops the default /, static: true removes the server with its API routes and load endpoints, and staticData keeps client navigations working.

for a principal

Choose per section of the product: what must be fresh per request, what can be built ahead, and whether keeping any server is worth it for a mostly static marketing site.

## Three ways a page can reach the browser An AnalogJS app can mix three rendering outcomes, all configured in the `analog()` plugin call in `vite.config.ts`: | Outcome | When the HTML is made | Needs a running server | Marketing-site example | |---|---|---|---| | **Server-rendered per request** (SSR) | on every request | yes | `/catalogue`, whose prices change | | **Prerendered** (static site generation) | once, at build time | no, for that page | `/`, `/blog`, each post | | **Client-only** | in the browser, from an empty shell | no rendering on the server | `/admin/**` | ## Server rendering is the default The `analog()` platform plugin applies `ssr: true` before your own options, and Analog's docs say it plainly: SSR is enabled by default. So a page such as `/catalogue` is rendered by Angular on the server for each request, and its `.server.ts` `load` runs as part of that request. You only write `ssr: false` to opt the **whole** app out, which produces a client-only build. ## Prerendering with prerender.routes `prerender.routes` accepts an array, or an async function that returns one. Each entry can be: 1. **A string** such as `'/blog'`: that URL is rendered to an HTML file at build time. 2. **A route config** `{ route: '/pricing', staticData: true }`: also writes the page's `load` result as a static response, so client-side navigations to that page work without a server. 3. **A content directory** `{ contentDir: 'src/content/blog', transform }`: Analog reads each Markdown file's front matter and calls `transform(file)`, which returns the URL to prerender, or `false` to skip it (for example, drafts). `recursive: true` descends into subfolders. Other `prerender` options: - `discover: true` also crawls links found in the rendered pages. - `sitemap: { host }` writes a `sitemap.xml` for the prerendered routes. - `postRenderingHooks` run a function on each rendered route, for example to inline critical CSS. **The default:** when `prerender.routes` is not given, Analog prerenders `/` only. The moment you supply your own list, that default is gone, which is why Analog's docs tell you to keep `'/'` in it. An empty array opts out of prerendering entirely. ## Client-only routes with routeRules Per-URL behaviour goes in **Nitro's `routeRules`**, passed through the `nitro` option: ```ts nitro: { routeRules: { '/admin/**': { ssr: false }, }, }, ``` For matching URLs Analog's server skips rendering and returns the app's plain `index.html` shell; Angular then boots and renders in the browser. That suits pages that depend on browser-only state, or that gain nothing from server HTML. ## static: true removes the server `static: true` means "prerender the static pages without producing the server output". The build is a folder of files (`dist/analog/public`) for any static host. Analog's docs note that `ssr` must still be `true`: prerendering renders each listed page with the server renderer at build time. What you give up: - **Per-request rendering**: `/catalogue` becomes whatever it was at build time, or is not served at all if it was not listed. - **API routes**: nothing answers `/api/...` except files you prerendered. - **Client-side data loading**: a prerendered page's first load carries its `load` data, but a later client navigation requests `/api/_analog/pages/...`, which only exists if that route was prerendered with `staticData: true`. ## Choosing per section of the marketing site 1. **Home, pricing and blog posts** change when the team publishes, so prerender them and add `sitemap` so crawlers find every post. 2. **The catalogue** shows prices and stock that change between builds, so leave it to per-request SSR and let its `load` run each time. 3. **The admin area** is behind a login and needs no crawlable HTML, so mark it client-only with `routeRules`. 4. **The newsletter signup** is an API route, so the site needs the Nitro server anyway, and `static: true` is off the table. ## Pitfalls - Listing only the blog in `prerender.routes` and losing the prerendered `/` without noticing. - Using `ssr: false` to make one area client-only; it switches off SSR for the whole app, whereas `routeRules` scopes it. - Choosing `static: true` for a site that has a signup API route or a live catalogue. - Reaching for streaming SSR (`experimental.streaming`, new in 2.7): it is experimental, needs Angular 21 or later and a server entry that uses `renderStream`, so it is not a default to recommend.

  • In AnalogJS, what does staticData: true on a prerender route add?
    It also prerenders the page's server `load` result, written as a static response at that page's `/api/_analog/pages/...` URL. Without it, only the first load of a prerendered page carries its data; a later client-side navigation requests the endpoint, which a static-only deployment cannot answer.
  • In AnalogJS 2.7, should you turn on streaming SSR for the marketing site?
    Not by default. Streaming SSR arrived in 2.7.0 behind `experimental.streaming`, needs Angular 21 or later, and has no effect unless `main.server.ts` uses `renderStream`. On an older Angular the plugin warns and falls back to buffered SSR. Treat it as an experiment you measure, not a baseline.
  • In AnalogJS, why can a prerendered page still be server-rendered for some URLs?
    Prerendering writes files for the URLs you list; the server keeps rendering everything else while `ssr` is on. A blog post missing from the list, for example one added after the build, is rendered on request by the Nitro server, unless the build is `static: true`, in which case nothing serves it.

saying these in an interview costs you the question

  • Analog renders on the client only unless ssr: true is passed to analog().
  • Setting ssr: false in analog() is how you make one section client-only.
  • Adding prerender.routes keeps the default prerendered / as well.
  • static: true keeps a small server for API routes and load endpoints.
  • Streaming SSR is the recommended default in Analog 2.7.