Angular Integration
Learn how to use OPTIMAGE with Angular. Create standalone components, services, pipes, and directives for optimized responsive images.
Introduction
OPTIMAGE integrates seamlessly with Angular. Since OPTIMAGE serves standard image URLs, no additional packages are required. Leverage Angular's component architecture for clean, reusable image handling.
No dependencies required! OPTIMAGE provides standard image URLs that work with any Angular setup - standalone components, NgModules, and SSR with Angular Universal.
Basic Usage
The simplest way to use OPTIMAGE in Angular is with a standard img element:
import { Component } from '@angular/core';
@Component({
selector: 'app-hero',
standalone: true,
template: `
<img
src="https://cdn.optimage.com/col_abc123/768/hero-banner.webp"
[srcset]="srcset"
sizes="100vw"
alt="Hero banner"
loading="lazy"
/>
`
})
export class HeroComponent {
srcset = `
https://cdn.optimage.com/col_abc123/320/hero-banner.webp 320w,
https://cdn.optimage.com/col_abc123/768/hero-banner.webp 768w,
https://cdn.optimage.com/col_abc123/1280/hero-banner.webp 1280w
`;
}OptimageService
Create a service to centralize URL generation logic:
// services/optimage.service.ts
import { Injectable } from '@angular/core';
export type OptimageSize = 320 | 384 | 448 | 512 | 576 | 672 | 768 | 896 | 1024 | 1152 | 1280;
@Injectable({ providedIn: 'root' })
export class OptimageService {
private readonly cdnUrl = 'https://cdn.optimage.com';
private readonly sizes: OptimageSize[] = [
320, 384, 448, 512, 576, 672, 768, 896, 1024, 1152, 1280
];
getUrl(collectionId: string, slug: string, size: OptimageSize = 768): string {
return `${this.cdnUrl}/${collectionId}/${size}/${slug}.webp`;
}
getSrcset(collectionId: string, slug: string): string {
return this.sizes
.map(size => `${this.getUrl(collectionId, slug, size)} ${size}w`)
.join(', ');
}
getImageUrls(collectionId: string, slug: string) {
return {
src: this.getUrl(collectionId, slug, 768),
srcset: this.getSrcset(collectionId, slug),
};
}
}Usage
import { Component, inject } from '@angular/core';
import { OptimageService } from './services/optimage.service';
@Component({
selector: 'app-product-card',
standalone: true,
template: `
<img
[src]="imageUrls.src"
[srcset]="imageUrls.srcset"
sizes="(max-width: 768px) 100vw, 33vw"
[alt]="product.name"
loading="lazy"
class="w-full h-48 object-cover rounded-lg"
/>
`
})
export class ProductCardComponent {
private optimage = inject(OptimageService);
product = { name: 'Product', slug: 'product-image' };
imageUrls = this.optimage.getImageUrls('col_products', this.product.slug);
}OptimizedImage Component
Create a reusable standalone component:
// components/optimized-image.component.ts
import { Component, Input, inject, computed, signal } from '@angular/core';
import { OptimageService } from '../services/optimage.service';
@Component({
selector: 'app-optimized-image',
standalone: true,
template: `
<img
[src]="src()"
[srcset]="srcset()"
[sizes]="sizes"
[alt]="alt"
[loading]="priority ? 'eager' : 'lazy'"
[decoding]="priority ? 'sync' : 'async'"
[class]="className"
/>
`
})
export class OptimizedImageComponent {
private optimage = inject(OptimageService);
@Input({ required: true }) collectionId!: string;
@Input({ required: true }) slug!: string;
@Input({ required: true }) alt!: string;
@Input() sizes = '100vw';
@Input() priority = false;
@Input() className = '';
src = computed(() =>
this.optimage.getUrl(this.collectionId, this.slug)
);
srcset = computed(() =>
this.optimage.getSrcset(this.collectionId, this.slug)
);
}Usage
import { Component } from '@angular/core';
import { OptimizedImageComponent } from './components/optimized-image.component';
@Component({
selector: 'app-gallery',
standalone: true,
imports: [OptimizedImageComponent],
template: `
<div class="grid grid-cols-3 gap-4">
@for (image of images; track image.slug; let i = $index) {
<app-optimized-image
collectionId="col_gallery"
[slug]="image.slug"
[alt]="image.alt"
sizes="(max-width: 768px) 100vw, 33vw"
[priority]="i < 3"
className="w-full aspect-square object-cover rounded-lg"
/>
}
</div>
`
})
export class GalleryComponent {
images = [
{ slug: 'sunset', alt: 'Beautiful sunset' },
{ slug: 'mountain', alt: 'Mountain view' },
{ slug: 'ocean', alt: 'Ocean waves' },
];
}OptimageUrl Pipe
Create a pipe for simple URL generation in templates:
// pipes/optimage-url.pipe.ts
import { Pipe, PipeTransform, inject } from '@angular/core';
import { OptimageService, OptimageSize } from '../services/optimage.service';
@Pipe({
name: 'optimageUrl',
standalone: true,
})
export class OptimageUrlPipe implements PipeTransform {
private optimage = inject(OptimageService);
transform(slug: string, collectionId: string, size: OptimageSize = 768): string {
return this.optimage.getUrl(collectionId, slug, size);
}
}
@Pipe({
name: 'optimageSrcset',
standalone: true,
})
export class OptimageSrcsetPipe implements PipeTransform {
private optimage = inject(OptimageService);
transform(slug: string, collectionId: string): string {
return this.optimage.getSrcset(collectionId, slug);
}
}Usage
import { Component } from '@angular/core';
import { OptimageUrlPipe, OptimageSrcsetPipe } from './pipes/optimage-url.pipe';
@Component({
selector: 'app-avatar',
standalone: true,
imports: [OptimageUrlPipe, OptimageSrcsetPipe],
template: `
<img
[src]="user.avatarSlug | optimageUrl:'col_avatars':320"
[srcset]="user.avatarSlug | optimageSrcset:'col_avatars'"
sizes="64px"
[alt]="user.name"
class="w-16 h-16 rounded-full"
/>
`
})
export class AvatarComponent {
user = { name: 'John Doe', avatarSlug: 'john-doe' };
}Optimage Directive
For maximum flexibility, create a directive that enhances existing img elements:
// directives/optimage.directive.ts
import { Directive, Input, ElementRef, OnInit, inject } from '@angular/core';
import { OptimageService } from '../services/optimage.service';
@Directive({
selector: 'img[optimage]',
standalone: true,
})
export class OptimageDirective implements OnInit {
private el = inject(ElementRef<HTMLImageElement>);
private optimage = inject(OptimageService);
@Input({ required: true }) optimage!: string; // collectionId
@Input({ required: true }) slug!: string;
@Input() optimageSizes = '100vw';
@Input() optimagePriority = false;
ngOnInit() {
const img = this.el.nativeElement;
const urls = this.optimage.getImageUrls(this.optimage, this.slug);
img.src = urls.src;
img.srcset = urls.srcset;
img.sizes = this.optimageSizes;
img.loading = this.optimagePriority ? 'eager' : 'lazy';
}
}Usage
import { Component } from '@angular/core';
import { OptimageDirective } from './directives/optimage.directive';
@Component({
selector: 'app-hero',
standalone: true,
imports: [OptimageDirective],
template: `
<img
optimage="col_hero"
slug="homepage-banner"
optimageSizes="100vw"
[optimagePriority]="true"
alt="Welcome to our site"
class="w-full h-96 object-cover"
/>
`
})
export class HeroComponent {}Using with NgOptimizedImage
You can use Angular's built-in NgOptimizedImage with OPTIMAGE by creating a custom loader:
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { IMAGE_LOADER, ImageLoaderConfig } from '@angular/common';
const optimageLoader = (config: ImageLoaderConfig) => {
// Expected src format: "collectionId/slug"
const [collectionId, slug] = config.src.split('/');
const width = config.width || 768;
return `https://cdn.optimage.com/${collectionId}/${width}/${slug}.webp`;
};
export const appConfig: ApplicationConfig = {
providers: [
{ provide: IMAGE_LOADER, useValue: optimageLoader },
],
};Usage
import { Component } from '@angular/core';
import { NgOptimizedImage } from '@angular/common';
@Component({
selector: 'app-product',
standalone: true,
imports: [NgOptimizedImage],
template: `
<img
ngSrc="col_products/product-photo"
width="400"
height="300"
alt="Product photo"
priority
/>
`
})
export class ProductComponent {}Note: NgOptimizedImage handles srcset generation automatically based on the loader. However, using the custom OptimizedImageComponent gives you more control over the exact sizes generated by OPTIMAGE.
Best Practices
- 1.Use standalone components: Modern Angular favors standalone components for better tree-shaking.
- 2.Inject services with inject(): Use the functional inject() API for cleaner code.
- 3.Use signals: Leverage Angular signals for reactive image URL generation.
- 4.Set priority for LCP: Use
priorityfor above-the-fold images.