Files
ISA-Frontend/.claude/skills/template-standards/references/defer-patterns.md
Lorenz Hilpert 7612394ba1 ♻️ refactor(claude): reorganize and rename skills for clarity
Consolidate and rename skills to shorter, more intuitive names:
- css-keyframes-animations → css-animations
- api-sync-manager → api-sync
- architecture-documentation → arch-docs
- jest-vitest-patterns → test-migration
- reactive-state-patterns → state-patterns
- library-scaffolder → library-creator

Merge related skills:
- angular-template + html-template → template-standards
- angular-effects-alternatives + ngrx-resource-api → state-patterns

Remove obsolete skills:
- architecture-enforcer (merged into architecture-validator)
- circular-dependency-resolver (merged into architecture-validator)
- standalone-component-migrator (merged into migration-specialist agent)
- swagger-sync-manager (replaced by api-sync)
- api-change-analyzer (merged into api-sync)
- type-safety-engineer (content distributed to relevant skills)
- test-migration-specialist (replaced by migration-specialist agent)

Add migration-specialist agent for standalone and test migrations.
Update all cross-references in CLAUDE.md and agents.
2025-12-11 11:30:05 +01:00

5.3 KiB

@defer Patterns

Lazy loading strategies and performance optimization.

Basic Patterns

Complete State Management

@defer (on viewport) {
  <app-product-reviews [productId]="productId()" />
} @placeholder (minimum 500ms) {
  <div class="skeleton" style="height: 400px;"></div>
} @loading (after 100ms; minimum 1s) {
  <mat-spinner />
} @error {
  <p>Failed to load reviews</p>
  <button (click)="retry()">Retry</button>
}

Triggers

Common Strategies

// Idle: Non-critical features
@defer (on idle) { <app-recommendations /> }

// Viewport: Below-the-fold
@defer (on viewport) { <app-comments /> }

// Interaction: User-initiated
@defer (on interaction) { <app-filters /> }

// Hover: Tooltips/popovers
@defer (on hover) { <app-user-tooltip /> }

// Timer: Delayed content
@defer (on timer(3s)) { <app-promo-banner /> }

// When: Custom condition
@defer (when userLoggedIn()) { <app-personalized-content /> }

Multiple Triggers

// OR logic: first trigger wins
@defer (on interaction; on timer(5s)) {
  <app-newsletter-signup />
}

Prefetching

// Load JS on idle, show on interaction
@defer (on interaction; prefetch on idle) {
  <app-video-player />
}

// Load JS on hover, show on click
@defer (on interaction; prefetch on hover) {
  <app-modal />
}

Performance Patterns

Bundle Size Reduction

<div class="product-page">
  <!-- Critical: Load immediately -->
  <app-product-header [product]="product()" />

  <!-- Heavy chart: Defer on viewport -->
  @defer (on viewport) {
    <app-analytics-chart />
  } @placeholder {
    <div style="height: 300px;"></div>
  }

  <!-- Video player: Defer on interaction -->
  @defer (on interaction; prefetch on idle) {
    <app-video-player />
  } @placeholder {
    <img [src]="videoThumbnail" />
  }
</div>

Staggered Loading

<div class="dashboard">
  <app-header /> <!-- Immediate -->

  @defer (on idle) {
    <app-key-metrics /> <!-- Important -->
  }

  @defer (on viewport) {
    <app-recent-activity /> <!-- Secondary -->
  } @placeholder {
    <div style="height: 400px;"></div>
  }

  @defer (on viewport) {
    <app-analytics /> <!-- Tertiary -->
  } @placeholder {
    <div style="height: 300px;"></div>
  }
</div>

Conditional Defer (Mobile Only)

// Component
shouldDefer = computed(() => this.breakpoint([Breakpoint.Tablet]));

// Template
@if (shouldDefer()) {
  @defer (on viewport) { <app-heavy-chart /> }
} @else {
  <app-heavy-chart />
}

Requirements

Must Be Standalone

// ✅ Valid
@Component({ standalone: true })
export class ChartComponent {}

@defer { <app-chart /> } // Will defer

// ❌ Invalid
@NgModule({ declarations: [ChartComponent] })
@defer { <app-chart /> } // Won't defer! Loads eagerly

No External References

// ❌ Invalid: ViewChild reference
@ViewChild('chart') chart!: ChartComponent;
@defer { <app-chart #chart /> } // ERROR

// ✅ Valid: Use events
@defer {
  <app-chart (dataLoaded)="onChartLoaded($event)" />
}

Core Web Vitals

Prevent Layout Shift (CLS)

// ✅ Reserve exact height
@defer (on viewport) {
  <app-large-component />
} @placeholder {
  <div style="height: 600px;"></div>
}

// ❌ No height reserved
@defer (on viewport) {
  <app-large-component />
} @placeholder {
  <p>Loading...</p> // Causes layout shift
}

Don't Defer LCP Elements

// ❌ BAD: Hero image deferred
@defer (on idle) {
  <img src="hero.jpg" /> <!-- LCP element! -->
}

// ✅ GOOD: Load immediately
<img src="hero.jpg" />

@defer (on viewport) {
  <app-below-fold-content />
}

Improve Time to Interactive (TTI)

// Critical: Immediate
<button (click)="addToCart()">Add to Cart</button>

// Non-critical: Defer
@defer (on idle) {
  <app-social-share />
}

Common Pitfalls

1. Cascading Defer (Bad)

// ❌ Sequential loads
@defer (on idle) {
  <div>
    @defer (on idle) {
      <div>
        @defer (on idle) { <app-nested /> }
      </div>
    }
  </div>
}

// ✅ Single defer
@defer (on idle) {
  <div><div><app-nested /></div></div>
}

2. Above-Fold Defer

// ❌ Above-fold content deferred
<header>
  @defer (on idle) {
    <nav>...</nav> <!-- Should load immediately -->
  }
</header>

// ✅ Below-fold only
<header><nav>...</nav></header>
<main>
  @defer (on viewport) {
    <app-below-fold />
  }
</main>

3. Missing Minimum Durations

// ❌ Flickers quickly
@defer {
  <app-fast-component />
} @loading {
  <mat-spinner /> <!-- Flashes briefly -->
}

// ✅ Smooth loading
@defer {
  <app-fast-component />
} @loading (after 100ms; minimum 500ms) {
  <mat-spinner />
}

Real-World Example

<div class="product-page">
  <!-- Critical: Immediate -->
  <app-product-header />
  <app-product-images />
  <app-add-to-cart />

  <!-- Important: Idle -->
  @defer (on idle) {
    <app-product-description />
  }

  <!-- Below fold: Viewport -->
  @defer (on viewport) {
    <app-reviews />
  } @placeholder {
    <div style="min-height: 400px;"></div>
  }

  <!-- Optional: Interaction -->
  @defer (on interaction; prefetch on idle) {
    <app-size-guide />
  } @placeholder {
    <button>View Size Guide</button>
  }

  <!-- Related: Viewport -->
  @defer (on viewport) {
    <app-related-products />
  }
</div>