Configuring Server Routes by Hand

Angular has evolved considerably in recent years, with one of the most notable shifts being the move toward built-in server-side rendering. What was once handled by a separate library, Angular Universal, is now part of the core framework under @angular/ssr.

Today, every Angular application is server-side rendered by default, with SSR available as an opt-out feature. For many developers, the decision is binary: either the entire app needs SSR or it doesn't. A dashboard locked behind authentication might not benefit from SSR, while a public-facing e-commerce storefront almost certainly does.

But there are plenty of applications that fall somewhere in between. Consider an e-commerce site again: product pages are public and should be server-rendered, while checkout and order history pages sit behind a login wall. Mixing these requirements in a single app can create awkward situations.

If you apply SSR to a route that requires authentication, the server will render whatever the auth guard redirects to—typically the login page. Once the page loads in the browser, Angular detects the user is already authenticated and bounces them to the intended destination. The result is an undesirable flash of the login page before the real content appears, as illustrated below:

Exploring Routes Rendering Modes in Angular — figure 1

This flash happens because authentication state exists only in the browser. The server has no knowledge of the logged-in user, so it renders the fallback route. The browser, on the other hand, knows the user is authenticated and redirects accordingly.

Before Angular 19, developers facing this issue had limited options—disabling SSR entirely was often the only practical solution, even though it degraded the experience for other routes. The Angular team addressed this limitation by introducing per-route render modes, a feature commonly referred to as hybrid rendering.

Rather than forcing a single rendering strategy across the entire application, you can now assign a specific mode to each route. Additionally, Angular 19 introduced support for static site generation (SSG) on a per-route basis.

Let's walk through a concrete example.

Manual Setup of Server Routes

Keep in mind that this feature is currently in developer preview, so proceed with caution. Additional details about Angular's release and versioning policies can be found here.

First, confirm you're working with Angular 19. Angular introduces the concept of Server Routes, which allows you to specify rendering behavior for individual routes. Using the earlier scenario, imagine an application with two routes:

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    canActivate: [redirectIfNotLoggedInGuard],
  },
  {
    path: 'login',
    component: LoginComponent,
    canActivate: [redirectIfLoggedInGuard],
  },
];
Enter fullscreen mode Exit fullscreen mode

For the route configuration above, we can define server routes by pairing each path with the desired render mode. I prefer to keep these declarations alongside the route definitions, but a separate server routes file works just as well—it's entirely a matter of preference.

...
import { RenderMode, ServerRoute } from '@angular/ssr';

...

export const serverRoutes: ServerRoute[] = [
  {
    path: '',
    renderMode: RenderMode.Client,
  },
  {
    path: 'login',
    renderMode: RenderMode.Server,
  },
];
Enter fullscreen mode Exit fullscreen mode

The render mode property accepts one of three values: RenderMode.Client, RenderMode.Server, or RenderMode.Prerender. In this simple example, the home page is set to RenderMode.Client, meaning it won't be server-rendered, while the login page uses RenderMode.Server for server-side rendering.

One important note: every route must currently be listed in the Server Routes configuration.

After defining the routes, the final step is registering them with Angular's application config. This is done by calling the provideServerRouting function and passing in your server routes array.

...
import { provideServerRendering } from '@angular/platform-server';
import { provideServerRouting } from '@angular/ssr';
...

export const appConfig: ApplicationConfig = {
  providers: [
    ...
    provideServerRouting(serverRoutes),
    provideRouter(routes),
    ...
  ],
};
Enter fullscreen mode Exit fullscreen mode

That's all there is to it. Returning to our earlier example, the login page flash is now completely eliminated.

Exploring Routes Rendering Modes in Angular — figure 2

The full source code for this demo application is available here.

Configuration for New Projects

As you might expect from Angular, schematics can automate much of this setup. When scaffolding a new project, you can pass the --server-routing flag to enable server routing from the start, leaving you to simply configure your routes.

ng add @angular/ssr --server-routing
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

In this walkthrough, we explored Angular's Rendering Modes and how they can significantly improve the user experience. Previously, the choice was binary—either SSR everywhere or nowhere. Render modes change that equation, allowing you to apply SSR where it adds value, Prerender pages that are static, and keep CSR for routes where server rendering causes friction. This flexibility leads to a far more polished experience for end users.

That wraps things up for now. Thanks for reading, and happy coding until next time.