Skip to content

feat(@angular/build): add prerenderFormat option to prerender routes as <route>.html - #34180

Draft
JohannesHoppe wants to merge 1 commit into
angular:mainfrom
JohannesHoppe:feat/prerender-format
Draft

JohannesHoppe wants to merge 1 commit into
angular:mainfrom
JohannesHoppe:feat/prerender-format

Conversation

@JohannesHoppe

@JohannesHoppe JohannesHoppe commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

PR Checklist

Please check to confirm your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Prerendering always writes a route to <route>/index.html. On static hosts, this forces a choice between clean URLs and no redirects. You can't have both:

  • Clean URLs, but redirects: links use /<route>. Every direct request (search engines, bookmarks, shared links) is first redirected (301/308) to /<route>/, and the Angular router then removes the trailing slash again.
  • No redirects, but trailing slashes everywhere: links have to use /<route>/, and the app needs TrailingSlashPathLocationStrategy to keep the slash in the address bar.

Issue Number: #29173

What is the new behavior?

A new application builder option prerenderFormat makes both possible: clean URLs without a trailing slash, served directly with a status code 200.

  • "directory" (default): /foo/bar is written to foo/bar/index.html, unchanged.
  • "file": /foo/bar is written to foo/bar.html, so hosts that serve <route>.html for /<route> respond to /foo/bar without a redirect.

Mirrors Astro's build.format ('directory' | 'file'); Next.js, SvelteKit and Hugo offer the same choice via trailingSlash/uglyURLs.

  • The root route of the application and of each locale stays index.html, so / and /<locale>/ keep being served by the host's directory index.
  • "file" requires a build without a server. The @angular/ssr runtime looks up prerendered pages as <route>/index.html (AngularServerApp.buildServerAssetPathFromRequest, CommonEngine.retrieveSSGPage), and with a server there is no redirect to avoid, since the generated server.ts serves static files with redirect: false.
  • A route ending in index (for example /index or /Index) is reported as an error with "file", because its file would be index.html of the parent path.
  • The dev server does not prerender. When it switches a prerendering build to on-demand SSR, it resets the option. Otherwise, ng serve reports the same error as ng build.
  • It is a top-level option rather than part of prerender, because prerender is not considered when outputMode is set.

As suggested by @SanderElias in the issue, the option description states that not all hosting services support this. Happy to rename the option if you prefer a different name.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

As a stopgap, I built a builder (@angular-schule/prerender-format) that wraps @angular/build:application. It only works by replacing the internal prerenderPages() export of @angular/build at runtime to rename the output files, which is fragile and can break with any internal refactoring. A built-in option is the clean solution.

The option description in schema.json feeds the ng build reference on angular.dev. If this lands, I'm happy to follow up with a short section in the SSR guide ("Generate a fully static application") in angular/angular.

…s as `<route>.html`

Prerendered routes are written to `<route>/index.html`. Static hosts serve such a file under
`/<route>/`, so a request to `/<route>` is first redirected to the URL with a trailing slash,
which the Angular router then removes again.

The new `prerenderFormat` option of the application builder accepts `directory` (default,
unchanged behavior) and `file`, like the `build.format` option of Astro. With `file`, routes
are written to `<route>.html`, which avoids the redirect on hosts that serve `<route>.html`
for `/<route>`. The root route of the application and of each locale is still written to
`index.html`. Static redirect pages follow the same layout. A route whose last segment is
`index` is reported as a build error with `file`.

`file` is only supported when the build does not produce a server (for example with
`outputMode: "static"`), because the `@angular/ssr` runtime looks up prerendered pages as
`<route>/index.html`. The dev server resets the option when it switches prerendering to
on-demand SSR.

Closes angular#29173
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: @angular/build detected: feature PR contains a feature commit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant