Telerik blogs

Learn how the NgOptimizedImage directive can be used to optimize images in an Angular app by allowing developers to easily leverage features like lazy loading, image placeholders and automatic resizing.

Angular provides a broad suite of tools, APIs and libraries for building dynamic and responsive user interfaces. In a previous article, we’ve seen how Angular’s control flow blocks (@if, @for and @switch) allow us to easily manipulate the DOM, bind data and control the flow of our application’s user interface.

In today’s article, we’ll explore how the NgOptimizedImage directive can be used to optimize images in an Angular app.

NgOptimizedImage

An important aspect of creating a seamless user experience in web applications is optimizing images to ensure they load efficiently and effectively. NgOptimizedImage is a built-in Angular directive that can be used to simplify image optimization as it allows us to easily optimize images by compressing, resizing and caching them. This results in faster page loads, reduced bandwidth consumption and improved overall performance.

To use the NgOptimizedImage directive, we can first import it from the @angular/common directory:

import { NgOptimizedImage } from "@angular/common";

When imported, we can add the directive into the imports array of a certain Angular component or module.

import { NgOptimizedImage } from "@angular/common";

@Component({
  imports: [NgOptimizedImage],
})
export class ImageComponent {}

Once the NgOptimizedImage directive is included in the imports of your component or module, we can utilize the ngSrc attribute in your image elements to activate image optimization.

import { NgOptimizedImage } from "@angular/common";

@Component({
  template: `<img ngSrc="dog.png" alt="Descriptive alt text" />`,
  imports: [NgOptimizedImage],
})
export class ImageComponent {}

ngSrc replaces the standard src attribute, signaling Angular to handle the image using the optimizations provided by the NgOptimizedImage directive.

Priority

The NgOptimizedImage directive provides various configuration options to customize image optimization. As an example, for images critical to layout, such as Largest Contentful Paint (LCP) elements, the Angular documentation recommends always marking them with the priority attribute:

import { NgOptimizedImage } from "@angular/common";

@Component({
  template: `
    <img 
      priority 
      ngSrc="dog.png" 
      alt="Descriptive alt text" 
    />
  `,
  imports: [NgOptimizedImage],
})
export class ImageComponent {}

Marking an image with the priority tag prioritizes the image’s loading by using fetchpriority="high" and loading="eager". This not only prioritizes the image’s loading but also ensures the preloading of the image, which means it instructs the browser to download the image as soon as possible, even before processing the rest of the page content. This is particularly useful for images that are critical to maintaining a good user experience by reducing visual load times and improving perceived performance.

Width and Height

Setting the width and height of images helps maintain the layout stability of a webpage as these images load. Without these attributes, page content might shift unexpectedly as images load, which can negatively impact the user experience and cause layout shifts, often measured as Cumulative Layout Shift (CLS) in performance metrics.

In order to prevent unexpected layout shifts, the NgOptimizedImage directive requires us to specify the width and height attributes directly on our image element.

import { NgOptimizedImage } from "@angular/common";

@Component({
  template: `
    <img 
      width="600" 
      height="400"
      ngSrc="dog.png"
      alt="Descriptive alt text" 
    />
  `,
  imports: [NgOptimizedImage],
})
export class ImageComponent {}

Fill

The fill attribute can be used to adjust how images fit within their designated space in a responsive and visually appealing manner. This attribute is useful when dealing with images in a fluid or responsive layout where the size of the image container might change depending on the viewport or device (i.e., we want the image to “fill” a containing space).

import { NgOptimizedImage } from "@angular/common";

@Component({
  template: `
    <img 
      fill 
      ngSrc="dog.png" 
      alt="Descriptive alt text" 
    />
  `,
  imports: [NgOptimizedImage],
})
export class ImageComponent {}

When using the fill property, we should not specify explicit height or width attributes on the image element, as the intention is to allow the image to dynamically adapt to the size of its container, filling the space as needed based on the layout constraints.

Placeholders

The placeholder attribute is another useful feature that can be employed alongside the NgOptimizedImage directive to enhance image handling in Angular applications. This attribute helps to manage how images are displayed while they are loading, providing a better user experience by avoiding blank spaces or abrupt jumps in the layout.

Automatic Placeholders

If our setup includes a content delivery network (CDN) or an image hosting service that supports automatic resizing, the NgOptimizedImage directive allows us to utilize automatic placeholders. These are typically low-resolution versions of the original images that load quickly, providing a preview while the full-resolution image loads and can be applied by simply adding the placeholder attribute:

import { NgOptimizedImage } from "@angular/common";

@Component({
  template: `
    <img 
      placeholder
      ngSrc="dog.png"
      width="600"
      height="400"
    />
  `,
  imports: [NgOptimizedImage],
})
export class ImageComponent {}

Data URL Placeholders

For situations where an image loader is unavailable or unsuitable, a data URL can serve as a placeholder. This involves encoding a small version of the image directly in Base64 format:

import { NgOptimizedImage } from "@angular/common";

@Component({
  template: `
    <img
      placeholder="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAADSCA..."
      ngSrc="dog.png"
      width="600"
      height="400"
    />
  `,
  imports: [NgOptimizedImage],
})
export class ImageComponent {}

While this method can increase the initial load size of your HTML, it’s effective for critical images where you must ensure immediate availability without relying on external resources.

Non-blurred Placeholders

For scenarios where a more distinct, clear placeholder is preferred over the default blurred version, the NgOptimizedImage directive can be configured to use non-blurred placeholders. This option is particularly useful when maintaining a certain aesthetic or when the blurred effect may detract from the user interface design:

import { NgOptimizedImage } from "@angular/common";

@Component({
  template: `
    <img
      placeholder
      [placeholderConfig]="{ blur: false }"
      ngSrc="dog.png"
      width="600"
      height="400"
    />
  `,
  imports: [NgOptimizedImage],
})
export class ImageComponent {}

In this example, the [placeholderConfig]="{ blur: false }" configuration explicitly disables the blur effect on the placeholder. As a result, the placeholder image will retain its original clarity but might be displayed in lower resolution or simplified detail until the full-resolution image loads.

Loaders

An image loader is a function that generates an image transformation URL for a given image file. This can be helpful in optimizing how images are served while taking into account the specific conditions and requirements of each user’s device.

The NgOptimizedImage directive provides various loaders to support different image services and allows us to create custom loaders. There are three types of loaders that exist with NgOptimizedImage:

  • Generic Loader: Applies no transformations, returning the original image URL.
  • Third-Party Image Service Loaders: Preconfigured loaders for services like Cloudflare, Cloudinary, ImageKit, Imgix and Netlify.
  • Custom Loaders: Developer-defined loaders for unsupported image services or custom transformations.

To use a built-in loader (i.e., a third-party loader), we can add the provider factory to the providers array, passing the base URL for image assets as an argument. For example:

import { NgOptimizedImage, provideCloudinaryLoader } from '@angular/common'

@Component({
  template: `
    <img 
      ngSrc="dog.png"
      width="600"
      height="400"
    />
  `,
  imports: [NgOptimizedImage],
  providers: [provideCloudinaryLoader('https://res.cloudinary.com')]
})

To use a custom loader, we can provide a loader function as a value for the IMAGE_LOADER dependency injection token. The loader function takes an ImageLoaderConfig object as an argument and returns the absolute URL of the image asset. For example:

import { NgOptimizedImage, IMAGE_LOADER } from '@angular/common'

@Component({
  template: `
    <img 
      ngSrc="dog.png"
      width="600"
      height="400"
    />
  `,
  imports: [NgOptimizedImage],
  providers: [
    {
      provide: IMAGE_LOADER,
      useValue: (config: ImageLoaderConfig) => {
        return `https://example.com/images?src=${config.src}&width=${config.width}`;
      },
    },
  ]
})

Wrap-up

In this article, we explored how the NgOptimizedImage directive can be used to optimize images in an Angular app. By leveraging features like lazy loading, image placeholders and automatic resizing, developers can ensure that their apps are both efficient and visually appealing.

For more details on Angular’s image optimization capabilities, be sure to check out the official documentation on getting started with NgOptimizedImage!


About the Author

Hassan Djirdeh

Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.

Related Posts

Comments

Comments are disabled in preview mode.