Incremental Static Regeneration for Angular

By admin
Background photo by

Ani Kolleshi
PERSON

on Unsplash

In Angular v16 we got a new hydration system. It was also hinted that experiments for partial hydration and resumability are in the works. This is great news for Angular developers, but there is still a lot of work to be done to make Angular apps more performant and SEO friendly.

While

Hydration
ORG

and

Resumability
NORP

are great for improving the performance of our apps, they are not enough. Having to server-side render the application for each user request is not efficient and can be expensive for large applications with a lot of users.

Solutions like SSG (Static Site Generation) render the website at build time and cache the files for each app route.

SSG
ORG

has its own problems. For example, if a page is updated the entire site needs to be rebuilt. This can take a long time for large sites (if build caching is not applied).

That’s why we need a solution that combines the best of both worlds: server-side rendering the application for each user request and caching the pages while updating them when needed without having to rebuild the entire site.

And that’s where

Incremental Static Regeneration
ORG

(ISR) comes in.

ISR
ORG

is a technique that enables updating static pages of a site without having to rebuild the entire site.

How it works

The idea behind

ISR
ORG

is to server-side render the pages of our site at runtime, and then cache them for future requests. Instead of caching the pages forever, we can set a time limit for each page or route. When the time limit expires, we send the stale content to the user, and trigger a regeneration under the hood that will re-render the page and cache it again. We can update the pages of our site without having to rebuild the entire site.


ISR
ORG

can update the pages of a site when the data changes. Consider a blog. When a post is updated we can re-render the page representing the post and cache it again. This is also useful for e-commerce sites. Individual product pages can be updated when price changes happen or a product is out of stock. This is also called In-Demand Regeneration because the pages are regenerated only when they have changed.

Prerequisites

Before you can enable

ISR
ORG

in your application, the following steps need to be completed:

Enable Server-side rendering in your application.

(Optional) Include a custom cache handler (Filesystem,

Firebase Firestore
ORG

,

Redis
ORG

,

Cloudflare Workers KV
ORG

, etc.)

How to add

ISR
ORG

in Angular

We will use @rx-angular/isr (maintained by Push-based.io which offers also other services and tools to improve web performance and scalability) to add

ISR
ORG

in Angular. This package provides an API that allows you to add

ISR
ORG

with just a few lines of code.


First
ORDINAL

, install the library:

npm install

@rx-angular
PERSON

/isr

Next, add the

ISR
ORG

providers.

In a standalone app, we need to add the providers in the app.config.server.ts file:

+import { provideISR } from ‘@rx-angular/isr/server’;

const serverConfig: ApplicationConfig = {

providers: [


provideServerRendering
EVENT

(),

+ provideISR() // 👈 Register ISR providers

],

};

export const config = mergeApplicationConfig(appConfig,

serverConfig
GPE

);

In a

NgModule
FAC

based app, we need to add the providers in the AppServerModule:

+import { provideISR } from ‘@rx-angular/isr/server’;

@NgModule({

imports: [


AppModule
PERSON

,

ServerModule,

],

providers: [

+ provideISR() // 👈 Register ISR providers

],

bootstrap: [

AppComponent
PERSON

],

})

export class AppServerModule {}

These providers will allow us to modify the rendered HTML before it is sent to the user, and also to register the routes that we want to cache, and the time limit for each route. Link to source code.

Update the server.ts file to intercept the requests and send the cached pages if they exist.

+import {

ISRHandler
ORG

} from ‘@rx-angular/isr/server’;

export function app(): express.Express {

// Other Angular Universal setup code (removed for brevity)…

+ const isr = new

ISRHandler
ORG

({

+ indexHtml, // 👈 The index.html file

+ invalidateSecretToken: process.env[‘INVALIDATE_TOKEN’] || ‘

TOKEN
PERSON

‘, // 👈 The secret token used to invalidate the cache

+ enableLogging: !environment.production, // 👈 Enable logging in dev mode

+ });

+ server.get(‘*’,

+ async (req,

res
ORG

, next) => await isr.serveFromCache(req,

res
GPE

, next),

+ async (req,

res
ORG

, next) => await

isr.render(req
GPE

,

res
GPE

, next)

+ );

// remove Angular render handler as we will use the one from isr

+ (req, res) => {

+ res.render(indexHtml, { req, providers: [{ provide:

APP_BASE_HREF
GPE

,

useValue
NORP

: req.baseUrl }] });

+ }

return server;

}

Now you have added

ISR
ORG

to your Angular app.

How to use ISR

To use

ISR
ORG

, you need to add the revalidate property to the routes that we need to cache. This property accepts a number that represents the time limit for each route in

seconds
TIME

. For example, if you want to cache the home page for

10 seconds
TIME

, you can do it this way:

const routes: Routes = [

{

path: ”,

component:

HomeComponent
ORG

,

+ data: { revalidate:

10
CARDINAL

}

}

];

That’s it! All the building blocks are in place, and now you can use

ISR
ORG

in your Angular app.

NOTE: By default the library will use the In-Memory cache handler.

Outside of the features we’ve just explored

ISR
ORG

library also offers:

On-Demand Regeneration (regenerate the pages when the data changes, for example when a blog post is updated)

(regenerate the pages when the data changes, for example when a blog post is updated) Built-in cache handlers (In-memory and

Filesystem cache
PRODUCT

)

(In-memory and

Filesystem
ORG

cache) Plugin based cache handler (Build your own cache handler, for example

Redis
ORG

,

Cloudflare Workers KV
ORG

,

Firebase Firestore
ORG

, etc.)

(Build your own cache handler, for example

Redis
ORG

,

Cloudflare Workers KV
ORG

,

Firebase Firestore
ORG

, etc.) Error handling (If the page fails to render, we can send the stale content to the user, until the page is regenerated successfully)

(If the page fails to render, we can send the stale content to the user, until the page is regenerated successfully) Logging (We can enable logging in dev mode to see what’s happening under the hood)

(We can enable logging in dev mode to see what’s happening under the hood) Cache store/retrieve hooks (Update the cache before the page is stored in the cache, or before the page is retrieved from the cache to be sent to the user)

(Update the cache before the page is stored in the cache, or before the page is retrieved from the cache to be sent to the user) Combining Filesystem cache with Pre-rendering (We can use

ISR
ORG

with pre-rendering to pre-render the pages of our site, and then cache them for future requests)

Benefits

Improved TTFB metric (Time to first byte)

Less server resource usage because of caching

Avoiding doing the same work twice

Extendable APIs

A better developer experience (

DX
PERSON

)

The library is open source (

MIT
ORG

)

Gotchas

When using this package there are some considerations. Pages that are dependent on user specific data can be tricky to cache and re-use for all the users. In practice it may be better to not cache those pages.

Use

ISR
ORG

to improve your application experience.

ISR not only improves your application performance but also improves the experience for your users. Get started with

ISR
ORG


today
DATE

by checking it out at

https://www.rx-angular.io/docs/isr
DATE

.