What’s new in the Angular NgOptimizedImage directive

Alex Castle
Alex Castle

Just over a year ago theChrome Aurora team launched theAngular NgOptimizedImage directive. The directive is focused primarily on improving performance, as measured by theCore Web Vitals metrics. It bundles common image optimizations and best practices into a user-facing API that’s not much more complicated than a standard<img> element.

In 2023, we've enhanced thedirective with new features. This post describes the most substantial of those new features, with an emphasis on why we chose to prioritize each feature, and how it can help improve the performance of Angular applications.

New features

NgOptimizedImage has improved substantially over time, including the following new features.

Fill mode

Sizing your images by providing awidth andheight attribute is an extremely important optimization for reducinglayout shift, because browsers need to know the aspect ratio of the image in order to save room for it. However, sizing images is additional work for application developers, and doesn’t make sense with some image use cases.

Helping to resolve this tension is the first major feature added to the image component post-developer preview:fill mode. This is a way for developers to include images without explicitly sizing them, and without incurring layout shift.

With fill mode, the image sizing requirement is disabled, and the image is automatically styled to fill its containing element. This decouples an image’s aspect ratio from the space it occupies on the page, and gives you greater control over how images fit into your page layout.

Fill mode uses NgOptimizedImage as a better-performing alternative to thebackground-image css property. Place an image inside the<div> or other element that would have had thebackground-image styling, then enable fill mode, as demonstrated in the preceding code example. Use theobject-fit andobject-position CSS properties on the<div> to control how the image is positioned in the background.

// Height and width are required<img ngSrc="example.com" height="300" width="400">// Unless you use fill mode!<div>  <img ngSrc="example.com" fill></div>

Srcset generation

One of the most effective image optimization techniques isthe use of thesrcset attribute to ensure that properly-sized images are downloaded for any device that accesses your application. Usingsrcset throughout your app can prevent you from wasting bandwidth and substantially improve yourLCP Core Web Vital.

The downside to thesrcset attribute is that it can be cumbersome to implement. Manually writing outsrcset values means adding multiple lines of markup to each image element in your app, complete with multiple custom URLs for eachsrcset. You also have to decide on a set of breakpoints, which is complicated, as they can represent both screen densities and the viewport sizes of common devices.

That’s why addingautomated srcset generation into the NgOptimizedImage directive was a major post-launch milestone. With this addition, any application using a CDN that supports image resizing can get full, customizable, srcsets automatically added to every image generated with the NgOptimizedImage directive.

We’ve included a simplified API for setting thesizes property, which is used to ensure that each image gets the correct type ofsrcset. If you don’t include asizes attribute, we know that the image is meant to be fixed-size, and should get a density-dependent srcset, like the following:

<img src="www.example.com/image.png" srcset="www.example.com/image.png?w=400 1x, www.example.com/image.png?w=800 2x" >

This kind of srcset ensures that images are served at a size that takes the user’s device pixel density into account.

On the other hand, if you do include thesizes property,NgOptimizedImage generates a responsive srcset that includes breakpoints for many common device and image sizes, using this default list of breakpoints:

[16,32,48,64,96,128,256,384,640,750,828,1080,1200,1920,2048,3840]

Preconnect generation

To improve LCP, it’s important to reduce the time your users spend downloading the LCP image. In the previous section, you saw howsrcset can assist by transferring smaller image files, but an equally important optimization is to start the transfer as soon as possible. One way to do that is by usinglink rel="preconnect" tags to jump-start the connection to your image domain.

From the start, NgOptimizedImage has warned if you fail to preconnect to your LCP image’s domain, but warning isn’t the ideal solution–we’d rather just fix the problem for you. And that’s exactly what NgOptimizedImage now does, withautomated preconnect generation.

To support this feature, we use static code analysis to attempt to detect image domains in NgOptimizedImageloaders and automatically generate preconnect link tags for those domains. There still may be cases where manual preconnect links are required, but for most users, automatic preconnect means one less step needed for good image performance.

Enhanced support for custom loaders

A key element of NgOptimizedImage is the loader architecture, which allows the directive to automatically generate URLs that are tailored to the application’s image CDN. A set of built-in loaders is included for widely-used CDNs. We also provide for the use ofcustom loaders, which allow you to integrate NgOptimizedImage with nearly any image hosting solution.

At launch, these custom loaders were limited in scope, and could only read thewidth attribute from the image element. In response to user feedback, we added support for a customizableloaderParams data structure, which allows arbitrary data to be passed from the image element to the custom loader. With the expansion, custom loaders can be as simple or as complex as required by an application’s image infrastructure.

The following example shows how a simple custom loader could use theloaderParams API to select between two alternate image domains:

constmyCustomLoader=(config:ImageLoaderConfig)=>{if(config.loaderParams?.alternateDomain){return`https://alternate.domain.com/images/${config.src}`}return`https://primary.domain.com/images/${config.src}`;};

An example of a more complex custom loader is available in theAngular documentation.

Expanded guidance for image performance

Up until now, every image performance alert we’ve added to Angular has been part of the NgOptimizedImage directive. If you’re not using the directive in the app, you won’t get any guidance on image performance issues.

In Angular 17, we’re expanding the scope of image performance guidance to include all Angular apps. Now, if we detect image patterns that we know are performance-hurting mistakes, such as lazy-loading your LCP image, or downloading a file that’s much too big for the page, we’ll let you know, even if you’re not using NgOptimizedImage.

Image performance is important for all apps, and we’re excited to continue building out guardrails to help prevent common mistakes in Angular apps.

Looking forward

We’re already hard at work developing the next set of features for NgOptimizedImage. While image performance remains our central concern, we’d also like to add features that improve developer experience, to make sure that NgOptimizedImage remains an attractive option for including images in Angular applications.

One feature that's a priority for us is image placeholders. These are commonly used to make image loading look better on web applications, but can hurt performance if implemented incorrectly. We hope to build a performance-first image placeholder system into NgOptimizedImage. Stay tuned toour blog for further announcements!

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2023-11-15 UTC.