{"id":26016,"date":"2025-03-23T20:15:13","date_gmt":"2025-03-23T19:15:13","guid":{"rendered":"https:\/\/www.angulararchitects.io\/blog\/complete-guide-for-server-side-rendering-ssr-in-angular\/"},"modified":"2025-04-06T08:57:05","modified_gmt":"2025-04-06T06:57:05","slug":"guide-for-ssr","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/","title":{"rendered":"Updated: Guide for Server-Side Rendering (SSR) in Angular"},"content":{"rendered":"<div class=\"wp-post-series-box series-performance-pptimization wp-post-series-box--expandable\">\n\t\t\t<input id=\"collapsible-series-performance-pptimization6a02c8969f117\" class=\"wp-post-series-box__toggle_checkbox\" type=\"checkbox\">\n\t\n\t<label\n\t\tclass=\"wp-post-series-box__label\"\n\t\t\t\t\tfor=\"collapsible-series-performance-pptimization6a02c8969f117\"\n\t\t\ttabindex=\"0\"\n\t\t\t\t>\n\t\t<p class=\"wp-post-series-box__name wp-post-series-name\">\n\t\t\tThis is post 6 of 6 in the series <em>&ldquo;Performance Optimization&rdquo;<\/em>\t\t<\/p>\n\t\t\t<\/label>\n\n\t\t\t<div class=\"wp-post-series-box__posts\">\n\t\t\t<ol>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/why-is-initial-load-performance-so-important\/\">Why is Initial Load Performance so Important?<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-to-measure-initial-load-performance\/\">How to measure Initial Load Performance<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-to-use-angular-ssr-with-hydration\/\">How to use Angular SSR with Hydration<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/deferrable-views\/\">Improve Initial Load Time with Deferrable Views<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/defer-large-deps\/\">How to @defer 3rd party dependencies<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><span class=\"wp-post-series-box__current\">Updated: Guide for Server-Side Rendering (SSR) in Angular<\/span><\/li>\n\t\t\t\t\t\t\t<\/ol>\n\t\t<\/div>\n\t<\/div>\n<p>Updated on <em>Mar. 23rd, 2025<\/em> for <strong>Hybrid Rendering<\/strong> &amp; brandnew <strong>Incremental Hydration<\/strong> (now including demo) in <em>Angular v19.2<\/em>.<\/p>\n<p>This comprehensive post includes a quick introduction to SSR, a detailed setup guide and several best practices with <em>Angular v19<\/em> (released on <em>Nov 19th, 2024<\/em>), enhancing the <strong>initial load performance<\/strong> and thus the <strong>user experience<\/strong> of modern <strong>web applications<\/strong> built with <em>Angular<\/em>.<\/p>\n<p>If you haven't upgraded to v19 yet, then what are you waiting for? In my humble opinion, the new <strong>Hybrid Rendering<\/strong> and <strong>Incremental Hydration<\/strong> features of <em>v19<\/em> can already be used in production, even though they are still in <em>Developer Preview<\/em>:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/03\/ssr-features.png\" alt=\"Angular Features in Developer Preview (blue)\" \/><\/p>\n<p>See all Angular features in this <a href=\"https:\/\/www.angular.courses\/caniuse\">Angular feature roadmap<\/a> by <a href=\"https:\/\/www.gerome.dev\/\">Gerome Grignon<\/a>. By the way, if you want to use <em>Material<\/em> and\/or <em>CDK<\/em> with SSR, you need at least <em>v18<\/em>.<\/p>\n<p>The <em>Angular team<\/em> has recently (well actually for quite some time) been putting in <a href=\"https:\/\/angular.dev\/roadmap#fast-by-default\">a huge effort<\/a> and doing a fantastic job to help us improve the initial load time. SSR plays a significant role in achieving that goal for our framework of choice. Read my post from July 2023 to learn <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/why-is-initial-load-performance-so-important\/\">why initial load performance is so crucial<\/a> for your <em>Angular<\/em> apps.<\/p>\n<h2>Essentials<\/h2>\n<p>Let's start with the basics. You can, of course, skip this section if you're already familiar with SSR, and continue with the next section about <strong>building<\/strong>.<\/p>\n<h3>Server-Side Rendering (SSR)<\/h3>\n<p>Server-Side Rendering (SSR) is a web development technique where the (in our case node) server generates <strong>the HTML content<\/strong> of a web page (in our case with JavaScript), providing faster initial load time. This results in a smoother user experience, especially for those on slower networks (e.g. onboard a train in \ud83c\udde9\ud83c\uddea or \ud83c\udde6\ud83c\uddf9 \u2013 which I happen to be a lot recently \ud83d\ude0f) or low-budget devices. Additionally, it improves SEO and crawlability for Social Media and other bots like the infamous ChatGPT.<\/p>\n<p>New <em>Angular CLI<\/em> projects will automatically prompt SSR (since <em>Angular v17<\/em>):<\/p>\n<pre><code class=\"language-bash\">ng new your-fancy-app-name<\/code><\/pre>\n<p>For existing projects simply run the <code>ng add<\/code> command (since <em>Angular v17<\/em>):<\/p>\n<pre><code class=\"language-bash\">ng add @angular\/ssr<\/code><\/pre>\n<p><strong>Warning<\/strong>: You might have to fix stuff manually (like adding imports of CommonJsDependencies) after adding SSR to your project \ud83d\ude2c<\/p>\n<p>Follow the <a href=\"https:\/\/angular.dev\/guide\/ssr#configure-server-side-rendering\">angular.dev guide<\/a> for detailed configuration. However, I'd recommend switching to the new Application Builder, which has SSR and SSG baked in (more on that in the <strong>build<\/strong> section below). Let's first clarify what SSG stands for.<\/p>\n<h3>Static Site Generation (SSG)<\/h3>\n<p>Static Site Generation (SSG) or Prerendering (like the Angular framework likes to call it), is the technique where HTML pages are prerendered <strong>at build time<\/strong> and then served as static HTML when a URL is visited. Instead of rendering live on demand, SSG generates the HTML once and serves the same pre-built HTML to all users. This provides even faster load times and further improves the user experience. However, since the HTML is being stored on the server, this approach is limited whenever dynamic content is needed.<\/p>\n<p><strong>Important note<\/strong>: For the use of SSG you don't need a node.js \/ express server. You can still ship your application from <a href=\"https:\/\/nginx.org\">nginx<\/a> or even Apache.<\/p>\n<h3>Full-application Hydration (preview in v16, stable since v17)<\/h3>\n<p>Hydration is the process where the prerendered static HTML, generated by SSR or SSG, is enhanced with interactivity on the client side. After the initial HTML is delivered and rendered in the browser, <em>Angular's JavaScript<\/em> takes over to &quot;hydrate&quot; the static content, attaching <strong>event listeners<\/strong> and thus making the page fully interactive. This approach combines the fast initial load times of SSR\/SSG with the dynamic capabilities of a SPA, again leading to a better overall user experience.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/angular-hydration.png\" alt=\"Angular Hydration\" \/><\/p>\n<p>Before <em>Angular's Hydration<\/em> feature, the prerendered static DOM would have been destroyed and replaced with the client-side-rendered interactive version, potentially resulting in a layout shift or a full browser window flash aka content flicker \u2013 both leading to bad results in performance tools like <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-to-measure-initial-load-performance\/\"><strong>Lighthouse<\/strong> and <strong>WebPageTest<\/strong><\/a>. In my opinion, <em>Angular SSR<\/em> was not production-ready until supporting Non-Destructive Hydration. This has changed in 2023 since this feature has already become stable in <em>Angular v17<\/em>.<\/p>\n<p>Please note: Since the introduction of <strong>Incremental Hydration<\/strong> in v19 (more on that later), the classic Hydration is referred to as <strong>Full-application Hydration<\/strong>. By the way, it's super easy to enable Hydration in <em>Angular<\/em> \ud83d\udca7<\/p>\n<pre><code class=\"language-typescript\">export const appConfig: ApplicationConfig = {\n  providers: [\n    provideClientHydration(), \/\/ use v16 full-app hydration\n  ],\n};<\/code><\/pre>\n<p>If you're still using <em>NgModules<\/em> (for reasons), it becomes:<\/p>\n<pre><code class=\"language-typescript\">@NgModule({\n  providers: [provideClientHydration()],\n})\nexport class AppModule {}<\/code><\/pre>\n<h3>Deferrable Views (preview in v17, stable since v18) with @defer<\/h3>\n<p><strong>Deferrable Views<\/strong>, also called <a href=\"mailto:code&gt;@defer&lt;\/code\">code>@defer<\/code<\/a> blocks, are <em>Angular's<\/em> native primitive for declaratively defer loading components of your application. It can be used as an alternative to the router-based lazy loading through <code>loadComponent()<\/code> (used with Standalone Components) or <code>loadChildren()<\/code> (used with another routes definition array and formerly used with <code>NgModules<\/code>).<\/p>\n<p>The <a href=\"mailto:code&gt;@defer&lt;\/code\">code>@defer<\/code<\/a> block allows you to specify when a component should be loaded and rendered, which can be based on various triggers like <code>on idle<\/code>, <code>on viewport<\/code>, <code>on hover<\/code>, <code>on interaction<\/code> etc. This is a great way to improve the initial load performance of your application by deferring the loading of non-critical components \u2013 for example everything below-the-fold \u2013 until they are needed. It can also be used to lazyload heavy libraries (like charts or complex tables). Learn more about the <a href=\"mailto:code&gt;@defer&lt;\/code\">code>@defer<\/code<\/a> block in my <a href=\"https:\/\/www.angulararchitects.io\/blog\/how-to-improve-initial-load-performance-with-angular-17s-deferrable-views\/\">blog post<\/a>.<\/p>\n<p><strong>Important<\/strong>: While <strong>Deferrable Views<\/strong> work completely independently of SSR, they can be used in combination with SSR to enforce <strong>client-side rendering (CSR)<\/strong> for certain components. This is especially useful for user-dependent content, such as user-specific lists or prices. The <a href=\"mailto:code&gt;@defer&lt;\/code\">code>@defer<\/code<\/a> block will render the placeholder on the server and then load the real content in the browser once it has been triggered.<\/p>\n<h4>Deferrable Views Demo<\/h4>\n<p>You can find a demo in the <code>deferrable views<\/code> branch in <a href=\"https:\/\/github.com\/L-X-T\/ssr-ih-ng19-days\">this repository<\/a> on GitHub \ud83d\ude0f<\/p>\n<h3>Event Replay (in preview since v18, but battle-proven by Google)<\/h3>\n<p>This example was taken from the official <a href=\"https:\/\/blog.angular.dev\/event-dispatch-in-angular-89d868d2351c\"><em>Angular blog<\/em><\/a>. Consider an app that contains a click button like this:<\/p>\n<pre><code class=\"language-html\">&lt;button type=&quot;button&quot; (click)=&quot;onClick()&quot;&gt;Click&lt;\/button&gt;<\/code><\/pre>\n<p>Previously, the event handler <code>(click)=&quot;onClick()&quot;<\/code> would only be called once your application has finished Hydration in the client. With Event Replay enabled, <strong><a href=\"https:\/\/github.com\/google\/jsaction\">JSAction<\/a><\/strong> is listening at the root element of the app. The library will capture events that (natively) bubble up to the root and replay them once Hydration is complete.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/angular-event-replay.png\" alt=\"Event Replay\" \/><\/p>\n<p>If implemented, <em>Angular<\/em> apps will stop ignoring events before Hydration is complete and allow users to <strong>interact with the page while it's still loading<\/strong>. There is no need for developers to do anything special beyond enabling this feature.<\/p>\n<p>And again, it's super comfy to enable Event Replay in your app \ud83e\udd29<\/p>\n<pre><code class=\"language-typescript\">export const appConfig: ApplicationConfig = {\n  providers: [\n    provideClientHydration(\n      withEventReplay(), \/\/ use hydration with v18 event replay\n    ),\n  ],\n};<\/code><\/pre>\n<p><strong>Note<\/strong>: At the time of writing, this feature is still in Developer Preview, so use it cautiously. However, I believe it's perfectly ready for production usage.<\/p>\n<h3>Hybrid Rendering (in preview since v19)<\/h3>\n<p><em>Angular v19<\/em> will introduce <strong>Hybrid Rendering<\/strong> to meet modern web demands following <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/56785\">this RFC<\/a> in the <em>Angular GitHup repo<\/em>. It allows providing additional route information for the server. Details like rendering modes and response headers will provide finer control for <strong>SSR<\/strong>.<\/p>\n<p>We can now select the <strong>page rendering mode<\/strong> per route:<\/p>\n<ul>\n<li><strong>SSR<\/strong>: The page renders on the server during a request (best for dynamic content that updates quickly)<\/li>\n<li><strong>SSG<\/strong>: The page renders during build time and is served as a static asset (best for UX<br \/>\n&amp; performance)<\/li>\n<li><strong>CSR<\/strong>: The page renders in the browser (best for user-based content)<\/li>\n<\/ul>\n<p>To do this we need to add a <code>serverConfig<\/code> in <code>app.config.server.ts<\/code> looking like this:<\/p>\n<pre><code class=\"language-typescript\">\/* src\/app\/app.config.server.ts *\/\n\/\/ imports [...]\n\nconst serverAppConfig: ApplicationConfig = {\n  providers: [provideServerRendering(), provideServerRoutesConfig(serverRoutes)],\n};\n\nexport const serverConfig = mergeApplicationConfig(appConfig, serverAppConfig);<\/code><\/pre>\n<p>This will be used in our <code>main.server.ts<\/code> instead of the client <code>appConfig<\/code>:<\/p>\n<pre><code class=\"language-typescript\">\/* src\/main.server.ts *\/\n\/\/ imports [...]\n\nconst bootstrap = () =&gt; bootstrapApplication(AppComponent, serverConfig);\n\nexport default bootstrap;<\/code><\/pre>\n<p>Next, we can specify the <code>renderMode<\/code> for each <code>serverRoute<\/code>:<\/p>\n<pre><code class=\"language-typescript\">\/* src\/app\/app.routes.server.ts *\/\n\/\/ imports [...]\n\nexport const serverRoutes: ServerRoute[] = [\n  { path: &quot;ssr&quot;, renderMode: RenderMode.Server },\n  { path: &quot;ssg&quot;, renderMode: RenderMode.Prerender },\n  { path: &quot;csr&quot;, renderMode: RenderMode.Client },\n];<\/code><\/pre>\n<p><strong>Note<\/strong>: The same routes need to be used in <code>app.routes.ts<\/code> to become fully functional.<\/p>\n<p>Additionally, some intelligent features like server side <strong>301 redirects<\/strong> or <strong>404 not found<\/strong> errors can be added to the server config.<\/p>\n<pre><code class=\"language-typescript\">\/* src\/app\/app.routes.server.ts *\/\n\/\/ imports [...]\n\nexport const serverRoutes: ServerRoute[] = [\n  \/\/ [...],\n  { path: &quot;redirect&quot;, renderMode: RenderMode.Server, status: 301 },\n  {\n    path: &quot;error&quot;,\n    renderMode: RenderMode.Server,\n    status: 404,\n    headers: {\n      &quot;Cache-Control&quot;: &quot;no-cache&quot;,\n    },\n  },\n  { path: &quot;**&quot;, renderMode: RenderMode.Server },\n];<\/code><\/pre>\n<p>If you want to play around with this, check out <a href=\"https:\/\/github.com\/jeanmeche\/ssr-v19\">this v19-ssr demo<\/a> by ma man <a href=\"https:\/\/github.com\/JeanMech\">Matthieu Riegler<\/a>.<\/p>\n<p><strong>Note<\/strong>: At the time of writing, this feature is still in Developer Preview, so implement it cautiously as the API may still change.<\/p>\n<h3>Incremental Hydration (in preview since v19)<\/h3>\n<p><strong>Partial Hydration<\/strong>, now called <strong>Incremental Hydration<\/strong>, announced at <a href=\"https:\/\/ng-conf.org\/\">ng-conf<\/a> and <a href=\"https:\/\/io.google\/2024\/explore\/7deddebc-3cae-4285-b2a9-affb5296102e\/\">Google I\/O<\/a> 2024, is a technique that allows incremental hydration of an app after server-side rendering, improving the initial load but also runtime performance by loading less JavaScript upfront. It builds upon the fabulous <a href=\"mailto:code&gt;@defer&lt;\/code\">code>@defer<\/code<\/a> API, in which we all fell in love since <em>Angular v17<\/em>. It's enabling <em>Angular<\/em> to render the HTML content on the server and hydrate deferred blocks on the client after they have been triggered to do so.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/03\/the-future-is-incremental-hydration.png\" alt=\"The FUTURE is incremental hydration\" \/><\/p>\n<p>The <em>Angular team<\/em> (special thanks goes out to <a href=\"https:\/\/github.com\/thePunderWoman\">Jessica Janiuk<\/a>! By the way, watch here presenation of <a href=\"https:\/\/www.youtube.com\/watch?v=v5KTJGEYLsM\">Incremental Hydration in Angular v19 on YouTube<\/a>) completed the <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/57664\">RFC<\/a> in the <em>Angular GitHup repo<\/em> and is now actively prototyping this feature, with an <em>experimental<\/em> release in <em>v19<\/em> for all performance-critical applications out there \ud83e\udd73<\/p>\n<p>To test it simply add <code>withIncrementalHydration()<\/code> to your <code>app.config.ts<\/code>:<\/p>\n<pre><code class=\"language-typescript\">import { provideClientHydration, withIncrementalHydration } from &quot;@angular\/platform-browser&quot;;\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    \/\/ [...]\n    provideClientHydration(withIncrementalHydration()),\n  ],\n};<\/code><\/pre>\n<p>Please note: <strong>Event Replay<\/strong> is automatically enabled when using <code>withIncrementalHydration()<\/code>, so you can remove that provider.<\/p>\n<p>Next, you can set a <strong>Hydration Trigger<\/strong> by choosing either:<\/p>\n<ul>\n<li><code>hydrate on<\/code> with a <strong>trigger<\/strong> (same as for <code>@defer<\/code>, see here for a <a href=\"https:\/\/www.angulararchitects.io\/blog\/how-to-improve-initial-load-performance-with-angular-17s-deferrable-views\/#triggers\">full list of built-in triggers<\/a>)<\/li>\n<li><code>hydrate when<\/code> with a <strong>boolean<\/strong> symbol, Signal or function as the trigger<\/li>\n<li><code>hydrate never<\/code> component will get rendered on the server but <strong>never hydrated<\/strong> (for static content)<\/li>\n<\/ul>\n<h4>hydrate on<\/h4>\n<pre><code class=\"language-html\">@defer (on viewport; prefetch on idle; hydrate on hover) {\n  &lt;app-deferred-hydration \/&gt;\n}<\/code><\/pre>\n<p>The component's static and SSRed HTML will be rendered before coming into the viewport, the corresponding JS bundle will be prefetched on idle (like PreloadingStrategy in the Router) and then finally the JS (all the interactivity, like event handlers) will be hydrated into the browser on hover. This is in stark contrast to the default full-application hydration, where all the components are hydrated at once.<\/p>\n<h4>hydrate when<\/h4>\n<pre><code class=\"language-html\">@defer (hydrate when isUserLoggedIn) {\n  &lt;app-deferred-hydration \/&gt;\n}<\/code><\/pre>\n<p>The component will be rendered on <code>idle<\/code> (default @defer trigger), i.e. after bootstrapping the App is complete, but it will only be hydrated once <code>isUserLoggedIn<\/code> is true.<\/p>\n<h4>hydrate never<\/h4>\n<pre><code class=\"language-html\">@defer (on viewport; hydrate never) {\n  &lt;app-deferred-hydration \/&gt;\n}<\/code><\/pre>\n<p>The component will be rendered on <code>viewport<\/code>, but it will never be hydrated - so no event handlers will be attached.<\/p>\n<h4>Incremental Hydration Demo<\/h4>\n<p>Make sure to play around with this fun-tastic feature starting from my <a href=\"https:\/\/github.com\/L-X-T\/ssr-ih-ng19-days\">Incremental Hydration Demo<\/a> on GitHub \ud83d\ude0f<\/p>\n<h4>Give back some love<\/h4>\n<p>Since this feature is still in <em>experimental<\/em> make sure to help further improve it by providing useful feedback to the <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/57664\">RFC<\/a> in the <em>Angular GitHup repo<\/em>.<\/p>\n<h2>Build<\/h2>\n<p>Since <em>Angular v17<\/em> we have two options for building our <em>Angular app<\/em>.<\/p>\n<h3>Angular's new Application Builder (all-in-one)<\/h3>\n<p>As mentioned, I'd recommend switching to the new Application Builder using <a href=\"https:\/\/esbuild.github.io\/\"><strong>esbuild<\/strong><\/a> and <a href=\"https:\/\/vitejs.dev\/\"><strong>Vite<\/strong><\/a>. The advantage of using esbuild over Webpack is that it offers faster build times and more efficient and fine-grained bundling. The significantly smaller bundle also leads to better initial load performance \u2013 with or without SSR! Vite is a faster development server supporting extremely fast Hot Module Replacement (HMR).<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/angular-vite-esbuild.png\" alt=\"Vite + esbuild\" \/><\/p>\n<p>Additionally, both <strong>SSR<\/strong> and <strong>Prerendering (SSG)<\/strong> are enabled by default as mentioned in this screenshot from the <em>Angular Docs<\/em> showing a table of the <em>Angular Builders<\/em> (note that the <a href=\"mailto:code&gt;@angular-devkit\/build-angular:server&lt;\/code\">code>@angular-devkit\/build-angular:server<\/code<\/a> is missing here):<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/angular-builder.png\" alt=\"Angular Builder\" \/><\/p>\n<p>Simply run <code>ng b<\/code> to trigger a <code>browser<\/code> and <code>server<\/code> build in one step. <em>Angular<\/em> will automatically process the Router configuration(s) to find all unparameterized routes and prerender them for you. If you want, you can add parameterized routes via <a href=\"https:\/\/angular.dev\/guide\/prerendering#prerendering-parameterized-routes\">a txt file<\/a>. To migrate, read my <a href=\"https:\/\/www.angulararchitects.io\/blog\/angular-17-update-control-flow-app-builder-migration\/\">automated App Builder migration guide<\/a>.<\/p>\n<h3>If still using Webpack (for reasons)<\/h3>\n<p>If \u2013 for any reason \u2013 you're still committed to using <strong>Webpack<\/strong> to build your web app, you need the browser builder to be configured in your <code>angular.json<\/code> (might be in <code>project.json<\/code> if you're using Nx). This will, of course, be added automatically once you run <code>ng add @angular\/ssr<\/code>.<\/p>\n<pre><code class=\"language-json\">{\n  &quot;server&quot;: {\n    &quot;builder&quot;: &quot;@angular-devkit\/build-angular:server&quot;,\n    &quot;options&quot;: {\n      &quot;outputPath&quot;: &quot;dist\/your-fancy-app-name\/server&quot;,\n      &quot;main&quot;: &quot;server.ts&quot;,\n      &quot;tsConfig&quot;: &quot;tsconfig.server.json&quot;\n    }\n  }\n}<\/code><\/pre>\n<p>Note: The referenced <code>server.ts<\/code> lies in the project's root and is the entry point of your server application. With this dedicated server builder, there is also a dedicated <code>tsconfig.server.json<\/code> (whereas the new Application Builder recommended previously merges the two tsconfig files for more convenience) \ud83e\udd13<\/p>\n<p>Now let's quickly have a look at the build scripts:<\/p>\n<p><strong>Important note<\/strong>: If you haven't started using <code>pnpm<\/code>, you're missing out. However, of course, both <code>npm run ...<\/code> and <code>yarn ...<\/code> will also work instead of <code>pnpm ...<\/code>.<\/p>\n<pre><code class=\"language-bash\">pnpm dev:ssr\nng run your-fancy-app-name:serve-ssr<\/code><\/pre>\n<p>Similar to <code>ng s<\/code>, which offers live reload during development, but uses server-side rendering. Altogether, it's a bit slower than <code>ng s<\/code> and won't be used a lot apart from quickly testing SSR on <code>localhost<\/code>.<\/p>\n<pre><code class=\"language-bash\">pnpm build:ssr\nng build &amp;&amp; ng run your-fancy-app-name:server<\/code><\/pre>\n<p>Builds both the <code>browser<\/code> application and the <code>server<\/code> script in production mode into the <code>dist<\/code> folder. Use this command when you want to build the project for deployment or run performance tests. For the latter, you could use <a href=\"https:\/\/www.npmjs.com\/package\/serve\">serve<\/a> or a similar tool to serve the application on your <code>localhost<\/code>.<\/p>\n<h2>Deploy<\/h2>\n<p>You have two options for deployment. While both are technically possible, I'd recommend using the second one.<\/p>\n<h3>Using on-demand rendering mode via node server<\/h3>\n<p>Starts the server for serving the application with node using SSR.<\/p>\n<pre><code class=\"language-bash\">pnpm serve:ssr\nnode dist\/your-fancy-app-name\/server\/main.js<\/code><\/pre>\n<p>I've shown a detailed <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-to-use-angular-ssr-with-hydration\/\">example Docker container here<\/a>.<\/p>\n<p><strong>Caution<\/strong>: <em>Angular<\/em> requires a certain <em>Node.js<\/em> version to run, for details see the <a href=\"https:\/\/angular.dev\/reference\/versions#actively-supported-versions\">Angular version compatibility matrix<\/a>.<\/p>\n<h3>Using build time SSR with SSG (recommended)<\/h3>\n<p>This option doesn't need a node environment on the server and is also way faster than the other one.<\/p>\n<pre><code class=\"language-bash\">pnpm prerender\nng run your-fancy-app-name:prerender<\/code><\/pre>\n<p>Used to generate an application's prerendered routes. The static HTML files of the prerendered routes will be attached to the <code>browser<\/code> build, not the <code>server<\/code>. Now you can deploy your <code>browser<\/code> build to whatever host you want (e.g. <a href=\"https:\/\/nginx.org\/en\/\"><strong>nginx<\/strong><\/a>). You're doing the same thing as without SSR with some extra directories (and <code>index.html<\/code> files).<\/p>\n<p><strong>Important note<\/strong>: If you're using the new (and recommended) Application Builder, you can skip these steps for building and prerendering since they're already included in <code>ng b<\/code>. In other words, you have zero extra work for building including SSR &amp; SSG \u2013 pretty great, huh? \ud83d\ude0e<\/p>\n<h2>Debug<\/h2>\n<p>The first step in debugging is looking for misconfigurations in your <code>angular.json<\/code> (<code>project.json<\/code>) or some errors in your <code>server.ts<\/code>. If both look good, there is no definite way to debug SSR and SSG issues. Feel free to <a href=\"mailto:alexander.thalhammer@angulararchitects.io\"><strong>contact me<\/strong><\/a> if you're experiencing any troubles.<\/p>\n<h3>How to avoid the most common issue<\/h3>\n<p>Browser-specific objects like <strong>document<\/strong>, <strong>window<\/strong>, <strong>localStorage<\/strong>, etc., do <strong>NOT<\/strong> exist on the <code>server<\/code> app. Since these objects are not available in a Node.js environment, trying to access them results in errors. This can be avoided by using the document injector or by running code explicitly in the browser:<\/p>\n<pre><code class=\"language-typescript\">import { Component, inject, PLATFORM_ID } from &quot;@angular\/core&quot;;\nimport { DOCUMENT, isPlatformBrowser, isPlatformServer } from &quot;@angular\/common&quot;;\n\nexport class AppComponent {\n  private readonly platform = inject(PLATFORM_ID);\n  private readonly document = inject(DOCUMENT);\n\n  constructor() {\n    if (isPlatformBrowser(this.platform)) {\n      console.warn(&quot;browser&quot;);\n      \/\/ Safe to use document, window, localStorage, etc. :-)\n      console.log(document);\n    }\n\n    if (isPlatformServer(this.platform)) {\n      console.warn(&quot;server&quot;);\n      \/\/ Not smart to use document here, however, we can inject it ;-)\n      console.log(this.document);\n    }\n  }\n}<\/code><\/pre>\n<h3>Browser-Exclusive Render Hooks<\/h3>\n<p>An alternative to injecting <code>isPlatformBrowser<\/code> are the two render hooks <code>afterNextRender<\/code> and <code>afterRender<\/code>, which can only be used within the <a href=\"https:\/\/angular.dev\/guide\/di\/dependency-injection-context\"><strong>injection context<\/strong><\/a> (basically field initializers or the constructor of a component):<\/p>\n<p>The <code>afterNextRender<\/code> hook, takes a callback function that runs <strong>once<\/strong> after the <strong>next<\/strong> change detection \u2013 a bit similar to the init lifecycle hooks. It's used for performing one-time initializations, such as integrating 3party libs or utilizing browser APIs:<\/p>\n<pre><code class=\"language-typescript\">export class MyBrowserComponent {\n  constructor() {\n    afterNextRender(() =&gt; {\n      console.log(&quot;hello my friend!&quot;);\n    });\n  }\n}<\/code><\/pre>\n<p>If you want to use this outside of the injection context, you'll have to add the injector:<\/p>\n<pre><code class=\"language-typescript\">export class MyBrowserComponent {\n  private readonly injector = inject(Injector);\n\n  onClick(): void {\n    afterNextRender(\n      () =&gt; {\n        console.log(&quot;you&#039;ve just clicked!&quot;);\n      },\n      { injector: this.injector },\n    );\n  }\n}<\/code><\/pre>\n<p>The <code>afterRender<\/code> hook, instead, is executed after <strong>every upcoming<\/strong> change detection. So use it with extra <strong>caution<\/strong> \u2013 same as you would do with the <code>ngDoCheck<\/code> and <code>ng[Content|View]Checked<\/code> hooks because we know that Change Detection will be triggered a lot in our <em>Angular<\/em> app \u2013 at least until we go <strong>zoneless<\/strong>, but that story that will be presented in yet another blog post \ud83d\ude0e<\/p>\n<pre><code class=\"language-typescript\">export class MyBrowserComponent {\n  constructor() {\n    afterRender(() =&gt; {\n      console.log(&quot;cd just finished work!&quot;);\n    });\n  }\n}<\/code><\/pre>\n<p>If you'd like to deep dive into these hooks, I recommend reading this <a href=\"https:\/\/netbasal.com\/exploring-angulars-afterrender-and-afternextrender-hooks-7133612a0287\">blog post<\/a> by Netanel Basal.<\/p>\n<h3>Angular Hydration in DevTools<\/h3>\n<p>The awesome <em>Angular<\/em> collaborator <a href=\"https:\/\/x.com\/Jean__Meche\">Matthieu Riegler<\/a> has recently added <strong>hydration debugging<\/strong> support to the <em>Angular's DevTools<\/em>! Which are, besides all Chromium derivatives, also available for Firefox, but then why would somebody still use that Boomer browser? \ud83d\ude0f<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/angular-SSR-hydration-in-dev-tools.png\" alt=\"Hydration in Angular DevTools\" \/><\/p>\n<p>Note the \ud83d\udca7 for hydrated components. Even though this feature was announced in the <em>Angular v18<\/em> update, it also works in past versions.<\/p>\n<h3>Other SSR Debugging Best Practices<\/h3>\n<p>Here is a collection of some more opinionated debugging recommendations:<\/p>\n<ul>\n<li><strong>DevTools<\/strong>: Besides the updated <em>Angular DevTools<\/em> tab, inspect your HTML with the <strong>Elements<\/strong> tab and your API requests with the <strong>Network<\/strong> tab. BTW, you should also simulate a slow connection here when performance testing your app.<\/li>\n<li><strong>Console<\/strong>: I personally like to log everything into my <strong>Console<\/strong>. Not interested in a logger lib since I'm fine with console.log() and maybe some other levels. Any console logs will be printed into the terminal where <code>ng b<\/code> or <code>pnpm dev:ssr<\/code> or <code>pnpm serve:ssr<\/code> has been run. We don't need to talk about logging into the browser's console on production, or do we?<\/li>\n<li><strong>Node.js<\/strong>: Start your SSR server with the --inspect flag to get more information: <code>node --inspect dist\/server\/main.js<\/code><\/li>\n<li><strong>Fetching<\/strong>: Ensure all necessary data is available at render time. Use <em><a href=\"https:\/\/angular.dev\/api\/core\/TransferState?tab=description\">Angular's TransferState<\/a><\/em> to transfer data from the server to the client.<\/li>\n<li><strong>Routing<\/strong>: Make sure all routes are correctly configured and match on both the <code>browser<\/code> and <code>server<\/code> builds.<\/li>\n<li><strong>Environments<\/strong>: Ensure environment variables are correctly set up for both <code>browser<\/code> and <code>server<\/code> builds.<\/li>\n<li><strong>3rd-party Libs<\/strong>: As always, be very careful about what you include in your project. Some libraries might not be implemented correctly and thus not work in an SSR context. Use conditional imports or platform checks to handle these cases or, even better, get rid of those libs in the first place.<\/li>\n<\/ul>\n<p>That's all I have got so far. If you've got anything to add, feel super free to <a href=\"mailto:alexander.thalhammer@angulararchitects.io\"><strong>contact me<\/strong><\/a>!<\/p>\n<h2>Advanced<\/h2>\n<h3>Disable Hydration for Components<\/h3>\n<p>Some components may not work properly with hydration enabled due to some issues, like DOM Manipulation. As a workaround, you can add the <code>ngSkipHydration<\/code> attribute to a component's tag to skip hydrating the entire component.<\/p>\n<pre><code class=\"language-html\">&lt;app-example ngSkipHydration \/&gt;<\/code><\/pre>\n<p>Alternatively, you can set <code>ngSkipHydration<\/code> as a host binding.<\/p>\n<pre><code class=\"language-typescript\">@Component({\n  host: { ngSkipHydration: &quot;true&quot; },\n})\nclass DryComponent {}<\/code><\/pre>\n<p>Please use this carefully and thoughtfully. It is intended as a last-resort workaround. Components that have to skip hydration should be considered bugs that need to be fixed.<\/p>\n<h3>Use Fetch API instead of XHR<\/h3>\n<p>The <strong><a href=\"https:\/\/web.dev\/articles\/introduction-to-fetch\">Fetch API<\/a><\/strong> offers a modern, promise-based approach to making HTTP requests, providing a cleaner and more readable syntax compared to the well-aged <strong>XMLHttpRequest<\/strong>. Additionally, it provides better error handling and more powerful features such as support for streaming responses and configurable request options. It's also recommended to be used with SSR by the <em><a href=\"https:\/\/stackoverflow.com\/questions\/77512654\/angular-detected-that-httpclient-is-not-configured-to-use-fetch-apis-angul\/77512684#77512684\">Angular team<\/a><\/em>.<\/p>\n<p>To enable it, simply add <code>withFetch()<\/code> to your <code>provideHttpClient()<\/code>:<\/p>\n<pre><code class=\"language-typescript\">export const appConfig: ApplicationConfig = {\n  providers: [provideHttpClient(withFetch())],\n};<\/code><\/pre>\n<p>If you're still using <em>NgModules<\/em> (for reasons), this becomes:<\/p>\n<pre><code class=\"language-typescript\">@NgModule({\n  providers: [provideHttpClient(withFetch())],\n})\nexport class AppModule {}<\/code><\/pre>\n<h3>Configure SSR API Request Cache<\/h3>\n<p>The <em>Angular HttpClient<\/em> will cache all outgoing network requests when running on the server. The responses are serialized and transferred to the browser as part of the server-side HTML. In the browser, <em>HttpClient<\/em> checks whether it has data in the cache and if so, reuses that instead of making a new HTTP request during the initial load. <em>HttpClient<\/em> stops using the cache once an application becomes stable in the browser.<\/p>\n<p>By default, HttpClient caches all <code>HEAD<\/code> and <code>GET<\/code> requests that don't contain <strong>Authorization<\/strong> or <strong>Proxy-Authorization<\/strong> headers. You can override those settings by using <code>withHttpTransferCacheOptions<\/code> when providing hydration:<\/p>\n<pre><code class=\"language-typescript\">export const appConfig: ApplicationConfig = {\n  providers: [\n    provideClientHydration(\n      withEventReplay(),\n      withHttpTransferCacheOptions({\n        filter: (req: HttpRequest&lt;unknown&gt;) =&gt; true, \/\/ to filter\n        includeHeaders: [], \/\/ to include headers\n        includePostRequests: true, \/\/ to include POST\n        includeRequestsWithAuthHeaders: false, \/\/ to include with auth\n      }),\n    ),\n  ],\n};<\/code><\/pre>\n<h3>Use Hydration support in Material 18 and CDK 18 \ud83d\udca7<\/h3>\n<p>Starting with <em>Angular Material 18<\/em>, all components and primitives are fully SSR and Hydration compatible. For information, read this <a href=\"https:\/\/blog.angular.dev\/material-3-experimental-support-in-angular-17-2-8e681dde650e\">blog post<\/a>. On how to upgrade your <em>Angular Material<\/em> app, consult the docs on <a href=\"https:\/\/material.angular.io\/guide\/material-2-theming#how-to-migrate-an-app-from-material-2-to-material-3\">migrate from Material 2 to Material 3<\/a>.<\/p>\n<h3>Combine SSR for static &amp; CSR for user content \ud83e\udd2f<\/h3>\n<p>As already mentioned above, with <em><strong>Angular v17 Deferrable Views<\/strong><\/em> you can easily mix SSR\/SSG with CSR \ud83c\udf89<\/p>\n<p>The usage is pretty straightforward: All <a href=\"mailto:code&gt;@defer&lt;\/code\">code>@defer<\/code<\/a> components will render their <a href=\"mailto:code&gt;@placeholder&lt;\/code\">code>@placeholder<\/code<\/a> on the server and the real content will be loaded and rendered once they have been triggered (by <em>on<\/em> or <em>when<\/em>) in the browser. Learn more about <a href=\"https:\/\/www.angulararchitects.io\/blog\/how-to-improve-initial-load-performance-with-angular-17s-deferrable-views\/\">how to use and trigger Deferrable Views<\/a>.<\/p>\n<p>Here are some primitive <strong>examples<\/strong> of how to combine SSR and CSR:<\/p>\n<ul>\n<li>Static pages: Use SSR (SSG)<\/li>\n<li>Static content with live updates: Use deferred components for the live content and SSR for the rest<\/li>\n<li>Product list with prices depending on the user: Defer price components and use SSR for the rest<\/li>\n<li>List with items depending on the user: Defer the list component and use SSR for the rest<\/li>\n<\/ul>\n<p>So basically, everywhere you need CSR (for user-dependent content), you need to <a href=\"mailto:code&gt;@defer&lt;\/code\">code>@defer<\/code<\/a> those parts. Use the <a href=\"mailto:code&gt;@placeholder&lt;\/code\">code>@placeholder<\/code<\/a> (and <a href=\"mailto:code&gt;@loading&lt;\/code\">code>@loading<\/code<\/a>) to show spinners or equivalents to inform the user that something is still being loaded. Also, make sure to reserve the right amount of space for the deferred components \u2013 avoid layout shifts at all costs!<\/p>\n<h3>SEO and Social Media Crawling \ud83d\udd0d<\/h3>\n<p>If you want to look good on Google and\/or social media platforms, make sure to implement all the necessary <strong>meta tags<\/strong> in SSR. For a comprehensive list, including some tools and tips, <a href=\"https:\/\/moz.com\/blog\/meta-data-templates-123\">jump here<\/a>.<\/p>\n<pre><code class=\"language-typescript\">export class SeoComponent {\n  private readonly title = inject(Title);\n  private readonly meta = inject(Meta);\n\n  constructor() {\n    \/\/ set SEO metadata\n    this.title.setTitle(&quot;My fancy page\/route title. Ideal length 60-70 chars&quot;);\n    this.meta.addTag({ name: &quot;description&quot;, content: &quot;My fancy meta description. Ideal length 120-150 characters.&quot; });\n  }\n}<\/code><\/pre>\n<h3>Use SSR &amp; SSG within AnalogJS \ud83d\ude80<\/h3>\n<p><a href=\"https:\/\/analogjs.org\/\">AnalogJS<\/a> is <em>the<\/em> meta-framework built on top of <em>Angular<\/em> \u2013 like <a href=\"https:\/\/nextjs.org\/\">Next.js<\/a> (React), <a href=\"https:\/\/nuxt.com\/\">Nuxt<\/a> (VueJS), <a href=\"https:\/\/start.solidjs.com\/\">SolidStart<\/a> (Solid). Analog supports SSR during development and building for production. If you want to know more, read the announcement of <a href=\"https:\/\/dev.to\/analogjs\/announcing-analogjs-10-19an\">version 1.0<\/a> by <a href=\"https:\/\/x.com\/brandontroberts\">Brandon Roberts<\/a> or wait for my <strong>upcoming blog post<\/strong> \ud83d\ude0f<\/p>\n<h3>Angular SSR &amp; SSG featuring I18n<\/h3>\n<p>Since the <em>Angular I18n<\/em> only works during built-time, it's fairly limited. Therefore, we recommend using <a href=\"https:\/\/jsverse.github.io\/transloco\/\">Transloco<\/a> (or <a href=\"https:\/\/github.com\/ngx-translate\/core\">NGX-Translate<\/a>). When adding Transloco by running <code>ng add @jsverse\/transloco<\/code>, you'll be prompted for SSR usage. However, you can also manually add the necessary changes for SSR (see <a href=\"https:\/\/jsverse.github.io\/transloco\/docs\/ssr-support\">Transloco Docs<\/a>):<\/p>\n<pre><code class=\"language-typescript\">@Injectable({ providedIn: &quot;root&quot; })\nexport class TranslocoHttpLoader implements TranslocoLoader {\n  private readonly http = inject(HttpClient);\n\n  getTranslation(lang: string) {\n    return this.http.get&lt;Translation&gt;(<code>${environment.baseUrl}\/assets\/i18n\/${lang}.json<\/code>);\n  }\n}<\/code><\/pre>\n<pre><code class=\"language-typescript\">export const environment = {\n  production: false,\n  baseUrl: &quot;http:\/\/localhost:4200&quot;, \/\/ &lt;== provide base URL for each env\n};<\/code><\/pre>\n<p>This will SSR everything in the default language and then switch to the user's language (if different) in the browser. While this generally works, <strong>it's definitely not ideal to see the text being swapped<\/strong>. Furthermore, we need to ensure there are <strong>no layout shifts<\/strong> upon switching! If you come up with any ideas on how to improve this, please <a href=\"mailto:alexander.thalhammer@angulararchitects.io\"><strong>contact me<\/strong><\/a>!<\/p>\n<h3>SSR and Hydration with Native Federation<\/h3>\n<p>The <strong>Angular Architects'<\/strong> native federation package now finally supports SSR for the shell app (starting with v18.2.3). This allows you to use <strong>Module Federation<\/strong> together with SSR and SSG in your <em>Angular<\/em> app. <a href=\"https:\/\/github.com\/angular-architects\/module-federation-plugin\/blob\/main\/libs\/native-federation\/README.md\"><strong>Native Federation<\/strong><\/a> has the same API as the webpack Module Federation but uses browser-native <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/import-maps-the-next-evolution-step-for-micro-frontends-article\/\">Import Maps<\/a> and is thus also working with <code>esbuild<\/code> \u2013 <em>Angular's<\/em> new Application Builder.<\/p>\n<p>Learn more about this approach and how to get started in <a href=\"https:\/\/devm.io\/angular\/microfrontend-module-federation-ssr-hydration\">this post<\/a> by <em>the master of module federation <strong>Manfred Steyer<\/strong><\/em>.<\/p>\n<h3>Caution with PWA<\/h3>\n<p>Be careful if you are using <em>Angular SSR<\/em> in combination with the <em>Angular PWA<\/em> service worker because the behavior deviates from default SSR. The initial request will be server-side rendered as expected. However, subsequent requests are handled by the service worker and thus client-side rendered.<\/p>\n<p>Most of the time that's what you want. Nevertheless, if you want a fresh request you can use the <code>freshness<\/code> option as <em>Angular PWA<\/em> <code>navigationRequestStrategy<\/code>. This approach will try a network request and fall back to the cached version of <em>index.html<\/em> when offline. For more information, consult the <em><a href=\"https:\/\/angular.dev\/ecosystem\/service-workers\/config#navigationrequeststrategy\">Angular Docs<\/a><\/em> and read this <a href=\"https:\/\/stackoverflow.com\/questions\/56383569\/how-to-make-angular-universal-and-pwa-work-together\/56400078#56400078\">response on Stack Overflow<\/a>.<\/p>\n<h2>Outlook<\/h2>\n<p>The next step will be <strong>streamed SSR<\/strong> for zoneless apps. To see what\u2019s planned for upcoming Angular releases, check the roadmap:<\/p>\n<ul>\n<li><a href=\"https:\/\/angular.dev\/roadmap#future-work-explorations-and-prototyping\">Angular roadmap on angular.dev<\/a><\/li>\n<\/ul>\n<h2>Performance Workshop<\/h2>\n<p>If you want to deep dive into Angular, we offer a variety of workshops \u2013 both in English and German.<\/p>\n<ul>\n<li><a href=\"https:\/\/www.angulararchitects.io\/en\/training\/angular-performance-optimization-workshop\/\"><strong>Performance Workshop<\/strong><\/a> \ud83d\ude80<\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/en\/training\/angular-best-practices\/\"><strong>Best Practices Workshop<\/strong><\/a> \ud83d\udcc8 (including performance related topics)<\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/en\/training\/angular-accessibility-workshop\/\"><strong>Accessibility Workshop<\/strong><\/a> \u267f<\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>In summary, implementing Server-Side Rendering (SSR) in <em>Angular<\/em> \u2014 along with Static Site Generation (SSG), hydration, and event replay \u2014 significantly enhances the initial load performance of your <em>Angular<\/em> applications. With the new Incremental Hydration feature, <em>Angular<\/em> has taken a significant step toward becoming the framework of choice for building high-performance web applications \u2013 something it has not traditionally been known for.<\/p>\n<p>This advancement results in a better user experience, particularly on slower networks or less capable devices, and it also improves your web app\u2019s SEO and crawlability. By following the guidelines and best practices outlined in this guide, you can boost your apps\u2019 load performance with minimal effort. Furthermore, the new Application Builder simplifies the process of building and deploying your projects.<\/p>\n<p>Feel free to <a href=\"https:\/\/alex.thalhammer.name\"><strong>contact me<\/strong><\/a> for further questions or join our <a href=\"https:\/\/www.angulararchitects.io\/en\/training\/angular-performance-optimization-workshop\/\"><strong>Performance Workshop \ud83d\ude80<\/strong><\/a> or the <a href=\"https:\/\/www.angulararchitects.io\/en\/training\/angular-best-practices\/\"><strong>Best Practices Workshop<\/strong><\/a> \ud83d\udcc8to learn more about performance optimization for <em>Angular<\/em> apps.<\/p>\n<p>This blog post was written by <a href=\"https:\/\/alex.thalhammer.name\/\">Alexander Thalhammer<\/a>. Follow me on <a href=\"https:\/\/github.com\/L-X-T\">GitHub<\/a>, <a href=\"https:\/\/twitter.com\/LX_T\">X<\/a> or <a href=\"https:\/\/at.linkedin.com\/in\/thalhammer\">LinkedIn<\/a>.<\/p>\n<h2>References<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/why-is-initial-load-performance-so-important\/\">Why is Initial Load Performance so Important?<\/a> by Alexander Thalhammer<\/li>\n<li><a href=\"https:\/\/blog.angular.dev\/angular-v16-is-here-4d7a28ec680d\">Angular v16 \u2013 official blog post<\/a> by Minko Gechev<\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/blog\/angular-17-update-control-flow-app-builder-migration\/\">Angular Update Guide to V17 incl. migrations<\/a> by Alexander Thalhammer<\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/blog\/how-to-improve-initial-load-performance-with-angular-17s-deferrable-views\/\">Angular v17\u2019s Deferrable Views<\/a> by Alexander Thalhammer<\/li>\n<li><a href=\"https:\/\/blog.angular.dev\/angular-v18-is-now-available-e79d5ac0affe\">Angular v18 \u2013 official blog post<\/a> by Minko Gechev<\/li>\n<li><a href=\"https:\/\/netbasal.com\/exploring-angulars-afterrender-and-afternextrender-hooks-7133612a0287\">Angular\u2019s after(Next)Render hooks<\/a> by Netanel Basal<\/li>\n<li><a href=\"https:\/\/blog.angular.dev\/event-dispatch-in-angular-89d868d2351c\">Angular Event Replay blog post<\/a> by Jatin Ramanathan &amp; Tom Wilkinson<\/li>\n<li><a href=\"https:\/\/www.youtube.com\/watch?v=v5KTJGEYLsM\">Angular Incremental Hydration on YouTube<\/a> by Jessica Janiuk<\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-to-use-angular-ssr-with-hydration\/\">Angular SSR Docker example<\/a> by Alexander Thalhammer<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This is post 6 of 6 in the series &ldquo;Performance Optimization&rdquo; Why is Initial Load Performance so Important? How to measure Initial Load Performance How to use Angular SSR with Hydration Improve Initial Load Time with Deferrable Views How to @defer 3rd party dependencies Updated: Guide for Server-Side Rendering (SSR) in Angular Updated on Mar. [&hellip;]<\/p>\n","protected":false},"author":21,"featured_media":26011,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-26016","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","post_series-performance-pptimization"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Updated: Guide for Server-Side Rendering (SSR) in Angular - ANGULARarchitects<\/title>\n<meta name=\"description\" content=\"This comprehensive post includes a quick introduction to SSR, a detailed setup guide and several best practices with Angular v19.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Updated: Guide for Server-Side Rendering (SSR) in Angular - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"This comprehensive post includes a quick introduction to SSR, a detailed setup guide and several best practices with Angular v19.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2025-03-23T19:15:13+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-04-06T06:57:05+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/sujet.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1000\" \/>\n\t<meta property=\"og:image:height\" content=\"563\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Alexander Thalhammer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/sujet.jpg\" \/>\n<meta name=\"twitter:creator\" content=\"@LX_T\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alexander Thalhammer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/\"},\"author\":{\"name\":\"Alexander Thalhammer\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/eefb0cd4d115dfd406a02b6dbc760d45\"},\"headline\":\"Updated: Guide for Server-Side Rendering (SSR) in Angular\",\"datePublished\":\"2025-03-23T19:15:13+00:00\",\"dateModified\":\"2025-04-06T06:57:05+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/\"},\"wordCount\":744,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/shutterstock_1319520923.jpg\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/\",\"name\":\"Updated: Guide for Server-Side Rendering (SSR) in Angular - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/shutterstock_1319520923.jpg\",\"datePublished\":\"2025-03-23T19:15:13+00:00\",\"dateModified\":\"2025-04-06T06:57:05+00:00\",\"description\":\"This comprehensive post includes a quick introduction to SSR, a detailed setup guide and several best practices with Angular v19.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/shutterstock_1319520923.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/shutterstock_1319520923.jpg\",\"width\":1000,\"height\":563},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Updated: Guide for Server-Side Rendering (SSR) in Angular\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/eefb0cd4d115dfd406a02b6dbc760d45\",\"name\":\"Alexander Thalhammer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/23f1b6f9b1ee7d04247b8320851762347d56c76b1537d100d07390d6d919b78d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/23f1b6f9b1ee7d04247b8320851762347d56c76b1537d100d07390d6d919b78d?s=96&d=mm&r=g\",\"caption\":\"Alexander Thalhammer\"},\"sameAs\":[\"https:\/\/x.com\/LX_T\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Updated: Guide for Server-Side Rendering (SSR) in Angular - ANGULARarchitects","description":"This comprehensive post includes a quick introduction to SSR, a detailed setup guide and several best practices with Angular v19.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/","og_locale":"en_US","og_type":"article","og_title":"Updated: Guide for Server-Side Rendering (SSR) in Angular - ANGULARarchitects","og_description":"This comprehensive post includes a quick introduction to SSR, a detailed setup guide and several best practices with Angular v19.","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/","og_site_name":"ANGULARarchitects","article_published_time":"2025-03-23T19:15:13+00:00","article_modified_time":"2025-04-06T06:57:05+00:00","og_image":[{"width":1000,"height":563,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/sujet.jpg","type":"image\/jpeg"}],"author":"Alexander Thalhammer","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/sujet.jpg","twitter_creator":"@LX_T","twitter_misc":{"Written by":"Alexander Thalhammer","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/"},"author":{"name":"Alexander Thalhammer","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/eefb0cd4d115dfd406a02b6dbc760d45"},"headline":"Updated: Guide for Server-Side Rendering (SSR) in Angular","datePublished":"2025-03-23T19:15:13+00:00","dateModified":"2025-04-06T06:57:05+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/"},"wordCount":744,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/shutterstock_1319520923.jpg","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/","name":"Updated: Guide for Server-Side Rendering (SSR) in Angular - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/shutterstock_1319520923.jpg","datePublished":"2025-03-23T19:15:13+00:00","dateModified":"2025-04-06T06:57:05+00:00","description":"This comprehensive post includes a quick introduction to SSR, a detailed setup guide and several best practices with Angular v19.","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/shutterstock_1319520923.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/07\/shutterstock_1319520923.jpg","width":1000,"height":563},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/guide-for-ssr\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Updated: Guide for Server-Side Rendering (SSR) in Angular"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/eefb0cd4d115dfd406a02b6dbc760d45","name":"Alexander Thalhammer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/23f1b6f9b1ee7d04247b8320851762347d56c76b1537d100d07390d6d919b78d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/23f1b6f9b1ee7d04247b8320851762347d56c76b1537d100d07390d6d919b78d?s=96&d=mm&r=g","caption":"Alexander Thalhammer"},"sameAs":["https:\/\/x.com\/LX_T"]}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/26016","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=26016"}],"version-history":[{"count":10,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/26016\/revisions"}],"predecessor-version":[{"id":29640,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/26016\/revisions\/29640"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/26011"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=26016"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=26016"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=26016"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}