Skip to content
    All posts
    Angular

    The Angular Performance Checklist I Run Before Every Release

    Lazy routes, deferrable views, bundle budgets, and image handling — the concrete list, in the order that pays off fastest.

    3 min readby

    This is the actual list, in the order I work through it. It's ordered by payoff-per-effort, not by how interesting the technique is.

    1. Check the bundle before you guess

    bash
    ng build --stats-json
    npx esbuild-visualizer --metadata dist/stats.json --open

    Nearly every time I've done this on a codebase for the first time, something absurd shows up. A full lodash import for one debounce. moment with all locales, 300KB, used to format one date. An icon library imported wholesale for six icons. These are ten-minute fixes with enormous returns, and no amount of change-detection tuning competes with deleting 300KB.

    2. Set budgets so it can't regress

    json
    "budgets": [
      { "type": "initial", "maximumWarning": "500kb", "maximumError": "800kb" },
      { "type": "anyComponentStyle", "maximumWarning": "4kb" }
    ]

    Fix the bundle, then set the budget just above where you landed, then make the build fail on error in CI. Otherwise you'll do this cleanup again in six months.

    3. Lazy-load every route that isn't the landing page

    ts
    export const routes: Routes = [
      { path: '', component: DashboardComponent },
      {
        path: 'payroll',
        loadComponent: () => import('./payroll/payroll.component')
          .then((m) => m.PayrollComponent),
      },
      {
        path: 'admin',
        loadChildren: () => import('./admin/admin.routes')
          .then((m) => m.ADMIN_ROUTES),
      },
    ];

    Admin screens are the classic offender — used by 2% of users, shipped to 100% of them. Pair this with withPreloading(PreloadAllModules) if you want the routes fetched during idle time after first paint; you get the small initial bundle and instant navigation.

    4. Defer what's below the fold

    html
    @defer (on viewport) {
      <app-analytics-chart [data]="chartData()" />
    } @placeholder (minimum 500ms) {
      <div class="chart-skeleton"></div>
    } @loading {
      <app-spinner />
    }

    Deferrable views are the highest-leverage Angular feature of the last few releases. A charting library is often the single heaviest dependency in an app, and it's usually rendered in a panel the user has to scroll to. on viewport means it downloads when they actually scroll there.

    Triggers worth knowing: on viewport, on interaction, on hover, on idle, and on timer. on interaction is perfect for a modal's contents — nobody needs the rich text editor until they click Edit.

    5. Use NgOptimizedImage for anything above the fold

    html
    <img ngSrc="/hero.jpg" width="1200" height="630" priority alt="Team dashboard" />

    It enforces width and height (killing layout shift), sets fetchpriority on priority images, lazy-loads the rest, and generates a srcset. LCP is usually an image, and this is the cheapest LCP fix available.

    6. Virtualise lists over ~100 rows

    The CDK's cdk-virtual-scroll-viewport renders only what's visible. Below a hundred rows it's not worth the complexity. Above a thousand, nothing else you do will matter.

    7. Self-host fonts and preload one weight

    A Google Fonts stylesheet costs a DNS lookup, a connection, and a render-blocking request before any text appears. Self-host, subset to the characters you use, font-display: swap, and preload only the weight in your LCP element.

    8. Then, and only then, tune change detection

    Template method calls, missing track, OnPush, zoneless. These matter — but they matter for interaction latency, not load performance, and load performance is what your users notice first and what Core Web Vitals measures.

    The measurement discipline

    Run Lighthouse on the production build, throttled to a mid-range Android on 4G. Not on your laptop, not on ng serve. The dev server serves unminified code without production optimisations, and your laptop is roughly ten times faster than the phone half your users hold. A site that feels instant on a MacBook can be genuinely unusable on a three-year-old Android — and you will never discover that from localhost:4200.

    AngularPerformanceCore Web VitalsBuild

    Keep reading

    PAPulapa Arun Kumar

    Full-stack developer building performant, clean, and user-friendly web & mobile applications. Also written as Arun Kumar Pulapa — same person, surname first.

    Built with

    ReactTypeScriptTailwind CSSVite

    © 2026 Pulapa Arun Kumar. All rights reserved.