On this page
Angular logo and text "cache busting assets" next to it

Cache busting in Angular for static assets

Find out how to make cache busting static files (images, pdfs, pngs, etc.) for the Angular application.

Images and other assets you have in your assets folder that you reference in Angular templates are not hashed, and we are going to solve that problem.

What is cache busting in Angular?

When building, Angular will add a hash to JavaScript, CSS, and assets referenced in the CSS files, but not to any other assets like pdfs, images, documents, and the like.

Cache busting is a method that lets the browser load the most recent version of a file instead of one that has already been cached. A static file that has been cached can be kept for a very long time until it eventually expires. When you update a website with the same file name, then the browser will provide not your fresh file but what’s available in the browser cache, so the user won’t be able to notice the modifications.

Cache busting solves the browser caching issue by using a unique file version identifier or by adding a unique URL parameter to the asset URL to tell the browser that a new version of the file is available. Therefore, the browser doesn’t retrieve the old file from the cache but rather makes an HTTP request to the origin server for the new file.

A solution to the cache busting problem in Angular

The solution would be to search for static assets, create a list of them with their hashes, and save the list as a JSON file.

This is useful when you want to change the same static asset without changing the name and ensure that the browser will always fetch the latest version.

Angular cache busting example result

Effortlessly manage and update static assets in your Angular application

In general, there might be very different solutions to that problem, but we are going to solve it in the following way:

  1. Install npm package @sitelintcode/angular-static-assets-hash.
  2. We’ll use the CLI command npx createAngularStaticHashes.
  3. By default, the package searches for static assets in the folder [angular root]/assets/images, but you can set any location through the parameter --staticAssetsPath=[path to static assets].
  4. The package gathers a list of all files from a given location, but you can change that by adding the parameter –-globPattern=[glob pattern]. Default: /**/*

The final result is the list of all assets collected in a single file called assets.json with a path and the file hash.

Example output of assets.json
{
  "assets/images/example.png": "iY5RY0G8wePLPRkZSTgW2XYZFZ7kQOqXoJTFpQFG5nI",
  "assets/images/avatar.svg": "7A7qFs_iOghsdUtLG-7y-5cxT3FC8S_BRXl5ldsNY7Y",
  "assets/images/body-background.svg": "K2FTBtDsxgKLQFr4BUW1ptnLWqPCKPyGypHCBTfcctQ",
  "assets/images/icons.svg": "Ka-ngr7Fht6ucmN03kzJeMN7f2iOtnkD-D63QJ01jhM"
}

Angular-based approach for cache busting static assets

Once the list of static assets is created, we need to have a way to use it with Angular. For that purpose, we can use an Angular Pipe.

Here is an example of the Pipe code:

Pipe code that adds a hash parameter to the asset URL
import { Pipe, PipeTransform } from '@angular/core';

import staticAssetsList from '../../assets.json';

@Pipe({
  name: 'fileHash'
})
export class FileHashPipe implements PipeTransform {

  private staticAssets: { [key: string]: string };

  constructor() {
    this.staticAssets = staticAssetsList;
  }

  private addParamsToUrl(givenUrl: string, urlParameters: string): string {

    if (typeof urlParameters !== 'string' || urlParameters.length === 0) {
      return givenUrl;
    }

    const urlSplitByHash: string[] = givenUrl.split('#');
    const hash: string = urlSplitByHash[1] || '';
    const params: string[] = urlParameters.split('&');
    let url: string = urlSplitByHash[0];

    if (url.indexOf('?') === -1) {
      url += '?';
    } else {
      url += '&';
    }

    url += params.map((paramItem: string): string => {
      const p: string[] = paramItem.split('=');

      return `${p[0]}=${window.encodeURIComponent(p[1])}`;
    })
      .join('&');

    url = url.slice(0, -1); // remove last &

    return hash ? `${url}#${hash}` : url;
  }

  private getHashForStaticAsset(assetPath: string): string {
    const path: string = assetPath.split('#')[0];

    if (typeof ResourceUtility.staticAssets[path] === 'string') {
      return this.addParamsToUrl(assetPath, `c=${this.staticAssets[path]}`);
    }

    return '';
  }

  public transform(filePath: string): string {
    const filePathWithCacheHash: string = this.getHashForStaticAsset(filePath);

    return filePathWithCacheHash;
  }
}

In Angular code, you can use it the following way:

URL parameter with a hash for the image
<img attr.src="{{ 'assets/images/example.png' | fileHash }}" alt="">

The result of the above code will give:

URL parameter with a hash for the image
<img src="assets/images/icons.svg?c=Ka-ngr7Fht6ucmN03kzJeMN7f2iOtnkD-D63QJ01jh" alt=""></use>

The hash is quite useful when we want to manage, e.g., one file with all <svg> icons. While you could change your single file with all <svg> icons all the time, your code remains the same.

URL parameter with a hash for the svg sprite
<svg aria-hidden="true" focusable="false" viewBox="0 0 48 48" width="32" height="32">
  <use attr.href="{{ 'assets/images/icons.svg#image-logo' | fileHash }}"></use>
</svg>

Share your feedback and improve the solution

The approach is one possible way to handle static assets and their cache busting. Feedback is very welcome.

Founded issue? Report it directly on Github @sitelintcode/angular-static-assets-hash repository.

Explore a real-world example of cache busting in Angular

You can see the implementation by looking at the source code of our SiteLint Platform, managed by the Angular framework.

Related posts

Comments

Leave a Reply

Real-user monitoring for Accessibility, Performance, Security, SEO & Errors (SiteLint)