skip to content

In AnalogJS, how do file names under src/app/pages become Angular routes for a marketing site's home page, blog, posts and 404 page?

level: middleimportance: must knowfreq 32%

answer

  1. only one suffix is collected
  2. parentheses erase a path segment
  3. square brackets become parameters
  4. a file and a folder, same name
  5. three dots catch the rest

basics

~20 s

In AnalogJS, only .page.ts files under src/app/pages become routes: (home).page.ts is /, blog/[slug].page.ts is /blog/:slug, a file beside a same-named folder is a layout, a (group) folder adds no segment, and [...page-not-found].page.ts is the ** route.

solid answer

~30 s

Analog collects every `*.page.ts` under `src/app/pages`; each must default-export its component and becomes a lazy-loaded Angular route. For a marketing site: `(home).page.ts` or `index.page.ts` is `/`; `pricing.page.ts` is `/pricing`; `blog/[slug].page.ts` (or `blog.[slug].page.ts`) is `/blog/:slug`; `blog.page.ts` beside a `blog/` folder is a layout whose `<router-outlet>` hosts `blog/(blog-list).page.ts` at `/blog` and the post pages; a `(legal)/` folder puts `privacy.page.ts` at `/privacy` without adding a segment; `[...page-not-found].page.ts` becomes the `**` route. Since 2.3, `[[...slug]].page.ts` is an optional catch-all. Guards, a title, resolvers or a redirect go in `export const routeMeta: RouteMeta`, which Analog merges into the generated route.

code

ts · 15 lines
ts
// src/app/pages/blog/[slug].page.ts  ->  /blog/:slug
import { Component, input } from '@angular/core';
import type { RouteMeta } from '@analogjs/router';

export const routeMeta: RouteMeta = {
  title: 'Blog',
};

@Component({
  template: `<h2>Post: {{ slug() }}</h2>`,
})
export default class BlogPostPage {
  // bound from the :slug parameter by provideFileRouter(withComponentInputBinding())
  readonly slug = input.required<string>();
}

go deeper

for a junior

Recall the suffix rule (.page.ts under src/app/pages) and the default export. Map a few file names to URLs: (home) or index for /, [slug] for a parameter.

for a middle

Explain layouts as a file plus a same-named folder with a router-outlet, pathless groups in parentheses, the ** catch-all and the optional catch-all, and how routeMeta merges into the generated route.

for a senior

Show you can debug a route tree: withDebugRoutes to inspect it, a real 404 status from the catch-all through injectResponse, and withExtraRoutes when a route cannot be expressed as a file.

for a principal

Weigh file-derived routes against a central route config for a large team: renames change URLs, but the folder tree becomes the single place to read the site map.

## What the file router collects AnalogJS builds the application's Angular routes from the **file system** instead of a hand-written `Routes` array. The router plugin scans `src/app/pages` (plus any `additionalPagesDirs` you configure) and keeps **only files whose names end in `.page.ts`**. Everything else in that folder, such as services, helpers or a page's `.server.ts` companion, is not a route. Two rules apply to every page file: - The component **must be the default export** (`export default class PricingPage {}`). In development Analog warns `Missing default export` when it finds neither a default export nor a redirect. - Every page is **lazy-loaded**: the generated route uses `loadChildren` to import the file only when the URL is visited. `provideFileRouter()` in `app.config.ts` registers the generated routes with Angular's router, so from then on it is ordinary Angular routing. ## File-name conventions | File under `src/app/pages` | URL | Kind | |---|---|---| | `(home).page.ts` or `index.page.ts` | `/` | index route | | `pricing.page.ts` | `/pricing` | static route | | `about/team.page.ts` or `about.team.page.ts` | `/about/team` | nested static route (folder or dot notation) | | `blog/[slug].page.ts` or `blog.[slug].page.ts` | `/blog/:slug` | dynamic route, parameter `slug` | | `(legal)/privacy.page.ts` | `/privacy` | route group: the folder adds no segment | | `[...page-not-found].page.ts` | `**` | catch-all | | `guides/[[...path]].page.ts` | `/guides` and anything below it | optional catch-all (2.3 and later) | Parentheses mean "this name is not part of the URL". Square brackets mean "this segment is a parameter", and the text inside becomes the parameter name you read from `ActivatedRoute` or, with `withComponentInputBinding()`, from a component input of the same name. A dynamic page reads its parameter the usual Angular ways: `inject(ActivatedRoute).paramMap`, or, once `withComponentInputBinding()` is passed to `provideFileRouter()`, an input with the same name as the bracketed segment (`slug` for `[slug].page.ts`). Nothing about reading parameters is Analog-specific; only the way the parameter is declared is. ## Layouts, groups and pathless layouts A **layout route** is a page file with a **folder of the same name** beside it: ```text src/app/pages/ blog.page.ts layout, renders <router-outlet> blog/ (blog-list).page.ts /blog [slug].page.ts /blog/:slug (legal).page.ts pathless layout (legal)/ privacy.page.ts /privacy terms.page.ts /terms ``` - `blog.page.ts` becomes the parent route at `blog`; the files inside `blog/` become its **children**, so its template needs a `<router-outlet>`. - `(blog-list).page.ts` is the **index child**: its parenthesised name contributes no segment, so it renders at `/blog` itself. - `(legal).page.ts` plus `(legal)/` is a **pathless layout**: `/privacy` and `/terms` share a wrapper without `/legal` appearing in the URL. - When siblings could match the same URL, Analog sorts them so plain segments come before `:param` segments and `**` comes last, so `blog/featured.page.ts` is tried before `blog/[slug].page.ts` whatever the order on disk. ## The 404 page and the status code `[...page-not-found].page.ts` becomes Angular's `**` route, so any unmatched URL renders it. By itself that only changes what the page shows. To make the server answer with **HTTP 404**, Analog's docs put a `canActivate` guard in the page's `routeMeta` that, during SSR (`import.meta.env.SSR`), sets `statusCode = 404` on the response returned by `injectResponse()` from `@analogjs/router/tokens` and then ends that response. Client-side navigations to an unknown URL never reach the server, so there the guard does nothing and the not-found component simply renders. ## Adding configuration to a generated route 1. **`routeMeta`**: `export const routeMeta: RouteMeta = { title, canActivate, resolve, meta, ... }` in the page file. Analog merges it into that page's generated route. `RouteMeta` deliberately excludes `path`, `component`, `children` and `loadChildren`, because the file system owns those. A `routeMeta` with `redirectTo` turns the file into a redirect route. 2. **`defineRouteMeta`**: the older helper is still exported but marked `@deprecated` in favour of the `RouteMeta` type; do not use it in new code. 3. **`withExtraRoutes(routes)`** passed to `provideFileRouter()` prepends hand-written routes, which take priority over the discovered ones. 4. **`withDebugRoutes()`** adds a page at `/__analog/routes` that lists the routes and layouts Analog generated from your files. ## Common mistakes - Naming a page `pricing.component.ts`: it is ignored because it lacks the `.page.ts` suffix. - Exporting the page class by name only: the route loads but has no component to render. - Creating `blog/` children without a `blog.page.ts` that renders a `<router-outlet>`, then wondering why there is no shared layout. Without the parent file the children are still routes, just without a wrapper. - Expecting `(marketing)/` to appear in the URL: parentheses always remove the segment.

  • In AnalogJS, how do you make the catch-all 404 page answer with an HTTP 404 status?
    The `[...page-not-found].page.ts` route only decides what renders. Add a `canActivate` guard in its `routeMeta` that, when `import.meta.env.SSR` is true, takes the response from `injectResponse()` (from `@analogjs/router/tokens`) sets `statusCode = 404` and ends the response, as Analog's docs show. In the browser there is no server response, so the guard just returns `true` and the not-found component renders.
  • In AnalogJS, how can you check the route table the file router generated?
    Pass `withDebugRoutes()` to `provideFileRouter()` in `app.config.ts`, then open `/__analog/routes` in the browser. It lists every page and layout Analog derived from the files, which is the quickest way to spot a missing `.page.ts` suffix or a layout without its folder.
  • In AnalogJS 2.7, what replaced defineRouteMeta?
    An exported constant typed with `RouteMeta`: `export const routeMeta: RouteMeta = { title: 'Pricing' }`. `defineRouteMeta` is still exported from `@analogjs/router` but marked `@deprecated`, so it belongs only in code you are migrating. `RouteMeta` also allows a redirect shape with `redirectTo`.

saying these in an interview costs you the question

  • Every TypeScript file in src/app/pages becomes a route, services included.
  • A page component can be a named export; Analog finds it by class name.
  • A folder name in parentheses is added to the URL.
  • A layout route is declared with a children array in routeMeta.
  • defineRouteMeta is the recommended way to give an Analog 2.7 page a title.