home / skills / gentleman-programming / gentleman-skills / performance

performance skill

/curated/angular/performance

This skill helps optimize Angular performance by leveraging NgOptimizedImage, @defer, lazy loading, and SSR strategies to improve loading and interactivity.

npx playbooks add skill gentleman-programming/gentleman-skills --skill performance

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
2.7 KB
---
name: angular-performance
description: >
  Angular performance: NgOptimizedImage, @defer, lazy loading, SSR.
  Trigger: When optimizing Angular app performance, images, or lazy loading.
metadata:
  author: gentleman-programming
  version: "1.0"
---

## NgOptimizedImage (REQUIRED for images)

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

@Component({
  imports: [NgOptimizedImage],
  template: `
    <!-- LCP image: add priority -->
    <img ngSrc="hero.jpg" width="800" height="400" priority>
    
    <!-- Regular: lazy loaded by default -->
    <img ngSrc="thumb.jpg" width="200" height="200">
    
    <!-- Fill mode (parent needs position: relative) -->
    <img ngSrc="bg.jpg" fill>
    
    <!-- With placeholder -->
    <img ngSrc="photo.jpg" width="400" height="300" placeholder>
  `
})
```

### Rules
- ALWAYS set `width` and `height` (or `fill`)
- Add `priority` to LCP (Largest Contentful Paint) image
- Use `ngSrc` not `src`
- Parent of `fill` image must have `position: relative/fixed/absolute`

---

## @defer - Lazy Components

```html
@defer (on viewport) {
  <heavy-component />
} @placeholder {
  <p>Placeholder shown immediately</p>
} @loading (minimum 200ms) {
  <spinner />
} @error {
  <p>Failed to load</p>
}
```

### Triggers

| Trigger | When to Use |
|---------|-------------|
| `on viewport` | Below the fold content |
| `on interaction` | Load on click/focus/hover |
| `on idle` | Load when browser is idle |
| `on timer(500ms)` | Load after delay |
| `when condition` | Load when expression is true |

```html
<!-- Multiple triggers -->
@defer (on viewport; on interaction) {
  <comments />
}

<!-- Conditional -->
@defer (when showComments()) {
  <comments />
}
```

---

## Lazy Routes

```typescript
// Single component
{
  path: 'admin',
  loadComponent: () => import('./features/admin/admin').then(c => c.AdminComponent)
}

// Feature with child routes
{
  path: 'users',
  loadChildren: () => import('./features/users/routes').then(m => m.USERS_ROUTES)
}
```

---

## SSR & Hydration

```typescript
bootstrapApplication(AppComponent, {
  providers: [
    provideClientHydration()
  ]
});
```

| Scenario | Use |
|----------|-----|
| SEO critical (blog, e-commerce) | SSR |
| Dashboard/Admin | CSR |
| Static marketing site | SSG/Prerender |

---

## Slow Computations

| Solution | When |
|----------|------|
| Optimize algorithm | First choice always |
| Pure pipes | Cache single result |
| Memoization | Cache multiple results |
| `computed()` | Derived signal state |

**NEVER** trigger reflows/repaints in lifecycle hooks (`ngOnInit`, `ngAfterViewInit`).

---

## Resources

- https://angular.dev/guide/image-optimization
- https://angular.dev/guide/defer
- https://angular.dev/best-practices/runtime-performance
- https://angular.dev/guide/ssr

Overview

This skill helps you optimize Angular app performance focusing on images, lazy loading, SSR/hydration, and defer-based lazy components. It codifies practical rules for NgOptimizedImage, @defer triggers, lazy routes, slow computation strategies, and when to choose SSR/CSR/SSG. Use it when you need measurable LCP, faster time-to-interactive, and lower bundle sizes.

How this skill works

It inspects image usage and recommends NgOptimizedImage with required attributes (ngSrc, width, height or fill) and LCP priority. It analyzes component loading patterns and suggests @defer triggers (viewport, interaction, idle, timer, condition) and lazy route patterns (loadComponent, loadChildren). It also guides SSR/hydration choices and slow-computation fixes like memoization, pure pipes, or computed signals.

When to use it

  • Optimizing Largest Contentful Paint (LCP) images and overall image loading
  • Deferring heavy or below-the-fold components to reduce initial bundle size
  • Converting routes to lazy-loaded components or feature modules
  • Choosing server-side rendering, hydration, or prerendering for SEO
  • Eliminating repeated expensive computations and reducing reflows

Best practices

  • Always use NgOptimizedImage: ngSrc and set width+height or use fill
  • Mark the LCP image with priority to avoid delayed paint
  • Wrap fill-mode images in a positioned parent (position: relative/fixed/absolute)
  • Defer heavy components using @defer with appropriate triggers and placeholders
  • Use lazy routes (loadComponent/loadChildren) to split bundles per feature
  • Avoid DOM-affecting work in lifecycle hooks; prefer memoization, pure pipes, or computed()

Example use cases

  • Photo-heavy marketing homepage: NgOptimizedImage for hero (priority) and lazy thumbnails
  • Comments widget below fold: @defer(on viewport) with a lightweight placeholder
  • Admin area: lazy routes via loadComponent to keep public bundle small
  • E-commerce product pages: SSR with provideClientHydration() for SEO and fast first paint
  • Dashboards with expensive transforms: memoize results or use pure pipes to prevent repeat work

FAQ

Do I always need width and height on images?

Yes. Always set width and height (or use fill) to prevent layout shifts and help NgOptimizedImage pick sizes.

When should I use @defer(on interaction) vs on viewport?

Use on interaction for features triggered by user action (click/hover/focus). Use on viewport for below-the-fold content that should load when scrolled into view.

When is SSR required instead of CSR?

Choose SSR for SEO-critical pages (blogs, product pages). Use CSR for private dashboards. Consider SSG/prerender for static marketing sites.