feat(@angular/build): add prerenderFormat option to prerender routes as <route>.html - #34180
Open
JohannesHoppe wants to merge 2 commits into
Open
JohannesHoppe wants to merge 2 commits into
JohannesHoppe wants to merge 2 commits into
Conversation
JohannesHoppe
force-pushed
the
feat/prerender-format
branch
from
September 25, 2026 18:10
5a28f55 to
e5d9701
Compare
JohannesHoppe
force-pushed
the
feat/prerender-format
branch
2 times, most recently
from
September 26, 2026 10:49
0da3613 to
e2d2305
Compare
…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 (after removing the `baseHref` option) is still written to `index.html`. Static redirect pages follow the same layout. A route that cannot be written safely to `<route>.html` keeps `<route>/index.html` and the build reports a warning: routes whose last segment is `index` in any casing, routes whose file would be the index output of the application, and routes whose file name is already used by another route. `file` is only considered 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`. When the build produces a server, a warning is reported and routes are written to `<route>/index.html`. Closes angular#29173
JohannesHoppe
force-pushed
the
feat/prerender-format
branch
from
September 26, 2026 12:43
e2d2305 to
a6dd90a
Compare
JohannesHoppe
marked this pull request as ready for review
September 26, 2026 13:35
There was a problem hiding this comment.
Code Review
This pull request introduces the "prerenderFormat" option to the Angular application builder, allowing prerendered pages to be output as ".html" files (using the "file" format) instead of the default "/index.html" structure. It includes robust conflict resolution logic for routes that cannot be safely written to ".html" (such as routes named "index" or matching the application's index file), falling back to the directory format with a warning. The review feedback suggests a performance optimization in "prerender.ts" to pre-lowercase the "indexOutput" variable once outside the route rendering loop, rather than performing redundant string operations inside "getFileFormatConflict" for every route.
…r routes as `<route>.html`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Checklist
Please check to confirm your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
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:/<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./<route>/, and the app needsTrailingSlashPathLocationStrategyto keep the slash in the address bar.Issue Number: #29173
What is the new behavior?
A new application builder option
prerenderFormatmakes both possible: clean URLs without a trailing slash, served directly with a status code 200."directory"(default):/foo/baris written tofoo/bar/index.html, unchanged."file":/foo/baris written tofoo/bar.html, so hosts that serve<route>.htmlfor/<route>respond to/foo/barwithout a redirect.Mirrors Astro's
build.format('directory' | 'file'); Next.js, SvelteKit and Hugo offer the same choice viatrailingSlash/uglyURLs.baseHrefoption) staysindex.html, so/and/<locale>/keep being served by the host's directory index.<route>.htmlkeeps<route>/index.html, and the build reports a warning that names the route. This applies to routes whose last segment isindexin any casing (for example/index,/Indexor/docs/index, whose file would be theindex.htmlof the parent path), to routes whose file would be the index output of the application (such asindex.csr.html), and to routes whose file name is already used by another route (compared case-insensitively)."file"is only considered when the build does not produce a server. The@angular/ssrruntime looks up prerendered pages as<route>/index.html(AngularServerApp.buildServerAssetPathFromRequest,CommonEngine.retrieveSSGPage), and with a server there is no redirect to avoid, since the generatedserver.tsserves static files withredirect: false. WithoutputMode: "server", orssrwithoutoutputMode, the build warns that the option is not considered and prerenders to<route>/index.html. The dev server does not prerender and shows no warning.prerender: whenoutputModeis set, theprerenderoption is not considered (options.ts), andoutputMode: "static"is the documented way to build a fully static application.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?
Other information
As a stopgap, I built a builder (
@angular-schule/prerender-format) that wraps@angular/build:application. It only works by replacing the internalprerenderPages()export of@angular/buildat 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.jsonfeeds theng buildreference 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.