install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/angular-lazy-loading" ~/.claude/skills/intense-visions-harness-engineering-angular-lazy-loading-406e50 && rm -rf "$T"
manifest:
agents/skills/codex/angular-lazy-loading/SKILL.mdsource content
Angular Lazy Loading
Reduce initial bundle size with loadComponent, loadChildren, preloading strategies, and deferrable views (@defer)
When to Use
- Splitting a large application into feature chunks that load on demand
- Reducing time-to-interactive for the initial page load
- Lazy-loading a single standalone component for a route without a dedicated route file
- Using
to defer heavy components below the fold until the user scrolls or interacts@defer - Configuring preloading to load non-critical routes in the background after initial load
Instructions
- Use
in route config for standalone components — the dynamicloadComponent
creates a code-split point:import(){ path: 'settings', loadComponent: () => import('./settings/settings.component').then(m => m.SettingsComponent) } - Use
with a separate routes file for feature areas with multiple routes — this creates a single chunk for the entire feature:loadChildren{ path: 'admin', loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES) } - Add preloading with
to load all lazy routes in the background after the app boots. For fine-grained control, usewithPreloading(PreloadAllModules)
fromQuicklinkStrategy
to preload only routes linked in the current viewport.ngx-quicklink - Use
blocks in templates to defer heavy components until needed. Combine with@defer
,@placeholder
, and@loading
blocks for smooth UX.@error - Use
for below-the-fold content,@defer (on viewport)
for on-demand panels, and@defer (on interaction)
for low-priority widgets.@defer (on idle) - Keep lazy chunk boundaries at feature-level route files — not individual components. Over-splitting creates waterfall loading.
- Analyze bundle output with
andng build --stats-json
to verify expected chunking.webpack-bundle-analyzer
// app.routes.ts — mixing loadComponent and loadChildren export const routes: Routes = [ { path: 'dashboard', loadComponent: () => import('./dashboard/dashboard.component').then((m) => m.DashboardComponent), canActivate: [authGuard], }, { path: 'admin', loadChildren: () => import('./admin/admin.routes').then((m) => m.ADMIN_ROUTES), canMatch: [adminGuard], }, { path: 'profile', loadComponent: () => import('./profile/profile.component').then((m) => m.ProfileComponent), }, ];
// admin/admin.routes.ts — feature route file export const ADMIN_ROUTES: Routes = [ { path: '', component: AdminDashboardComponent }, { path: 'users', loadComponent: () => import('./users/users.component').then((m) => m.UsersComponent), }, { path: 'reports', loadComponent: () => import('./reports/reports.component').then((m) => m.ReportsComponent), }, ];
// main.ts — preloading configuration bootstrapApplication(AppComponent, { providers: [ provideRouter(routes, withPreloading(PreloadAllModules), withComponentInputBinding()), ], });
<!-- @defer — deferrable views (Angular 17+) --> <main> <app-hero /> <!-- loads immediately, in critical bundle --> @defer (on viewport) { <app-product-recommendations /> @placeholder { <div class="skeleton" style="height: 200px"></div> } @loading (minimum 300ms) { <app-spinner /> } @error { <p>Failed to load recommendations.</p> } } @defer (on idle) { <app-chat-widget /> } </main>
Details
vs loadComponent
: loadChildren
loadComponent is ideal for a single route that maps to a single standalone component. loadChildren points to an entire route array — Angular loads the chunk once and registers all child routes. Use loadChildren when a feature has 3+ related routes to avoid multiple separate chunks for the same conceptual feature.
Preloading strategies:
| Strategy | Behavior |
|---|---|
(default) | Lazy routes load on demand only |
| All lazy routes preload after app bootstraps |
| Preloads routes linked in current viewport |
Custom | Full control — preload based on user role, connection speed, etc. |
Custom preloading strategy:
@Injectable({ providedIn: 'root' }) export class SelectivePreloadingStrategy implements PreloadingStrategy { preload(route: Route, load: () => Observable<unknown>): Observable<unknown> { return route.data?.['preload'] ? load() : EMPTY; } }
trigger options:@defer
— element enters the viewport (useson viewport
)IntersectionObserver
— user clicks or focuses the placeholderon interaction
— user hovers the placeholderon hover
— browser is idle (on idle
)requestIdleCallback
— after a delayon timer(2s)
— any boolean expression becomes truewhen condition
— prefetch the chunk while displaying placeholder synchronouslyprefetch on idle
Chunk naming: In
angular.json, set namedChunks: true to get human-readable chunk names in build output. Combined with bundleBudgets, this helps catch lazy routes that grow unexpectedly.
Testing lazy routes:
it('navigates to admin', async () => { await router.navigate(['/admin']); await fixture.whenStable(); expect(location.path()).toBe('/admin'); });
Source
https://angular.dev/guide/routing/lazy-loading-ngmodules
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.