Incremental Static Regeneration for Angular

Created on November 12, 2023 at 10:44 am

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 .

Connecting to blog.lzomedia.com... Connected... Page load complete