Improve App Performance with the NgOptimizedImage Directive
Tags: angular, performance, seo
Published: 2022-09-06 21:27:04

Newly released Angular 14.2 comes with a nifty image optimization feature that can be used to improve the performance of your application.

In this blog post, we will take a look at the exciting new `NgOptimizedImage` directive and explain how we used it to make this [example e-commerce application](https://e-commerce-scully-example.vercel.app/) perform up to 50% better on Lighthouse scores!

![image showing improved lighthouse score of Angular e-commerce app after implementing NgOptimizedImage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/je7ouvy70kwsz1lupbqz.png)

## What is NgOptimizedImage?
[NgOptimizedImage](https://angular.io/api/common/NgOptimizedImage) is a Directive focused on improving Image Loading performance. As a happy side-effect, `NgOptimizedImage` enforces best practices and improves Core Web Vitals scores, which might help your application rank higher on search engines.

**Important Note:** `NgOptimizedImage` is on [Developer Preview](https://angular.io/guide/releases#developer-preview). Features on Developer Preview might change APIs without notice. If you decide to use `NgOptimizedImage` on production, be aware when updating your application.

## How to Use NgOptimizedImage
In order to use the `NgOptimizedImage` directive, identifiable by the selector `img[rawSrc]`, you need to import `NgOptimizedImage` into your Module or Standalone Component.

```typescript
import { NgOptimizedImage } from '@angular/common';
```

### Set Up an Image Loader
Image Loaders are tools that host images and provide optimization services such as image resizing or compression.

Angular now provides popular Image Loader configurations out of the box. In our e-commerce application example, we set up [ImageKit](https://imagekit.io/) with the `provideImageKitLoader` function, whose `path` argument is the [default URL endpoint](https://imagekit.io/dashboard/url-endpoints) for an account.

```typescript
// src/app/app.module.ts

// ... other imports
import { NgOptimizedImage, provideImageKitLoader } from '@angular/common';

@NgModule({
  declarations: [AppComponent, HomeComponent, CartComponent],
  imports: [BrowserModule, AppRoutingModule, ScullyLibModule, NgOptimizedImage],
  providers: [provideImageKitLoader('https://ik.imagekit.io/fabioemoutinho')],
  bootstrap: [AppComponent],
})
export class AppModule {}
```

### Understand NgOptimizedImage Properties
`NgOptimizedImage` adds additional properties to an `img` tag:

- `rawSrc: string`: instead of `src` full URL, `rawSrc` represents the source image name

- `rawSrcset: string`: a comma-separated [list of width or pixel density descriptors](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset). Not required, but recommended. Usually used in conjunction with sizes attribute if you want to provide different image sources for different viewport sizes.

    ```html
    <!-- example taken from https://angular.io/api/common/NgOptimizedImage#properties -->

    <img rawSrc="hello.jpg" rawSrcset="100w, 200w" />

    <!-- img tag above would output an img tag similar to the img below -->
    <img src="path/hello.jpg" srcset="path/hello.jpg?w=100 100w, path/hello.jpg?w=200 200w" />
    ```

- `priority: boolean`: defaults to false; If true, adds [multiple optimizations](https://developer.chrome.com/blog/angular-image-directive/#mvp-for-the-ngoptimizedimage-directive) to the image

Images are going to be lazy-loaded by default. That is, it is going to be [up to the browser to decide when to load the images](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading). While lazy-loading an image is usually a good approach, [in some cases](https://web.dev/priority-hints/#increase-the-priority-of-the-lcp-image), like when you want to make sure the browser will download critical images first, you might want to set the `priority` property to `true` in order to increase the performance of your app.

The `NgOptimizedImage` directive also expects every image to have explicit `height` and `width` attributes.

Here are the changes we had to make on a component’s template to use the `NgOptimizedImage` directive.

```diff
  <!-- src/app/home/home.component.html ‐‐>

  <picture class="picture block relative overflow-hidden">
    <img
-     [src]="product.image"
+     [rawSrc]="product.id + '.jpg'"
      [alt]="product.title"
      class="img not-loaded object-contain w-full h-full absolute top-0 left-0"
      (load)="onLoadImage($event)"
      (error)="onErrorImage($event)"
+     width="200"
+     height="200"
    />
  </picture>
```

## Performance Results
In our example application, we removed `scully` to better measure the impact of a vanilla implementation of the `NgOptimizedImage` directive on the application’s home page. We tested two different scenarios:

1. https://e-commerce-no-scully-example-angular-13.vercel.app/ - Angular 13.3.11
2. https://e-commerce-no-scully-example.vercel.app/ - Angular 14.2.0, using `NgOptimizedImage`

We then ran Lighthouse locally (desktop) and had the following results:

| Metric                   | Angular 13.3.11 | Angular 14.2 with NgOptimizedImage | Impact        |
| ------------------------ | --------------- | --------------------------------- | ------------- |
| First Contentful Paint   | 0.4 s | 0.3 s | 25% decrease  |
| Speed Index              | 1.2 s | 0.6 s | 50% decrease  |
| Largest Contentful Paint | 1.7 s | 1.0 s | 41% decrease  |
| Time to Interactive      | 0.5 s | 0.4 s | 20% decrease  |
| Total Blocking Time      | 0 ms  | 0 ms  | -             |
| Cumulative Layout Shift  | 0.003 | 0.003 | -             |
| Performance              | 93    | 99    | 6.45% increase |

Even though you get excellent performance results with the automatic lazy loading of images, `NgOptimizedImage` shines even brighter when combining the usage of image loaders with the `rawSrcset` property and `sizes` attribute. Implementing the `rawSrcset` property and `sizes` attribute enables you to further optimize your application for different devices, especially for mobile devices.

## What’s Next for Angular’s Image Directive?
Chrome’s blog post announcement about `NgOptimizedImage` directive presents an interesting [roadmap](https://developer.chrome.com/blog/angular-image-directive/#future-roadmap), and we can expect many improvements like automatic `srcset` and `sizes` attributes for responsive images, automatic `height` and `width` attributes and more.

Angular v14.2 introduces other interesting features that we will talk about next, stay tuned on [Bitovi's Twitter](https://twitter.com/bitovi).

Need help staying up to date with the latest version of Angular to take advantage of these cool new features? [Contact us](https://www.bitovi.com/frontend-javascript-consulting/angular-consulting) for Angular Consulting help!