
Performance Optimization
Performance work is bottleneck diagnosis: measure what users feel, find the limiting stage, fix that stage, and verify the improvement.
Quick Navigation: Mental Model • Bottlenecks • Loading • Core Web Vitals • JavaScript • Rendering • Tools • Interview
Optimize Bottlenecks, Not Checklists
Frontend performance is not one thing. A page can be slow because HTML arrives late, the hero image is discovered late, JavaScript blocks the main thread, layout work repeats, or third-party scripts interrupt user interactions.
Step 1
Measure
Step 2
Diagnose
Step 3
Optimize
Step 4
Verify
Good engineers do not start with “use lazy loading.” They start with “which user-visible wait are we trying to remove?”
The Performance Cost Model
Map the symptom to the stage. This keeps optimization causal instead of cosmetic.
Network
Symptom
Slow first byte, late resources, heavy transfer size.
Inspect
Waterfall, TTFB, cache headers, CDN behavior, compression, request count.
Fix
Cache correctly, compress, prioritize critical resources, reduce redirects.
JavaScript
Symptom
Long tasks, delayed hydration, sluggish input, high INP.
Inspect
Main-thread flame chart, bundle analysis, long tasks, third-party scripts.
Fix
Split code, remove unused JS, defer non-critical work, move CPU work off thread.
Rendering
Symptom
Slow paint, layout thrashing, janky scroll or animation.
Inspect
Style/layout/paint costs, forced reflow, animation properties, DOM size.
Fix
Batch reads/writes, reduce layout complexity, virtualize, animate transform/opacity.
Media
Symptom
Poor LCP, high bytes, layout shifts, slow image decode.
Inspect
LCP element, image dimensions, srcset/sizes, format, priority, lazy loading.
Fix
Serve right size/format, reserve space, prioritize likely LCP media.
Loading Strategy
Loading performance is about discovery order and priority. The browser can only prioritize resources it knows about, and it can only render useful content once the critical dependencies arrive.
Good loading decisions
- Prioritize HTML, critical CSS, fonts, and likely LCP media.
- Lazy-load below-the-fold media and optional widgets.
- Prefetch likely next routes only when intent or idle time supports it.
- Use cache headers intentionally for immutable assets and dynamic HTML.
Bad loading decisions
- Preloading every important-looking resource.
- Lazy-loading the hero image or first-screen content.
- Shipping client-only shells for content that could be rendered earlier.
- Letting third-party scripts compete with first-party critical work.
Core Web Vitals as User Symptoms
Core Web Vitals are not optimization recipes. They are user-centered signals that point to different parts of the system. Treat them as diagnostics, then use traces and waterfalls to find the cause.
| Metric | What it means | Common causes | Likely fixes |
|---|---|---|---|
| LCP | How quickly the main content becomes useful. | Late HTML, slow server response, render-blocking CSS, late hero media discovery. | Improve TTFB, expose the LCP resource early, avoid lazy-loading the hero, reduce critical CSS delay. |
| INP | How responsive the page feels across interactions. | Long tasks, expensive event handlers, hydration work, large renders after input. | Break up work, reduce JavaScript, defer non-urgent updates, keep interaction handlers small. |
| CLS | How visually stable the page is after it starts loading. | Images without dimensions, injected banners, late font swaps, dynamic content above existing UI. | Reserve space, set media dimensions, avoid inserting content above the viewport, tune font loading. |
The mistake is treating the metric name as the root cause. “LCP is bad” is only the starting point. The useful answer is whether LCP is blocked by server response, resource discovery, resource download, or render delay.
JavaScript and Main-Thread Budget
JavaScript costs more than transfer size. The browser must download, parse, compile, and execute it, often on the same main thread needed for input, style, layout, and paint.
| Technique | Use when | Tradeoff |
|---|---|---|
| Route splitting | Different routes need different code. | Navigation may pay a chunk load cost. |
| Import on interaction | Heavy features are optional, such as editors or charts. | First use needs a loading state or prefetch strategy. |
| Worker offload | CPU-heavy work does not need direct DOM access. | Serialization and worker coordination add overhead. |
| Dependency trimming | A large library is used for a small capability. | Custom code has its own correctness and maintenance cost. |
Rendering, Media, and Interaction Work
Rendering performance shows up as slow interactions, layout shifts, dropped frames, or delayed content. This is where DOM size, CSS complexity, layout, paint, and media choices become user-visible.
Reduce unnecessary work
Virtualize long lists, avoid broad re-render boundaries, and keep derived work close to the data that changes.
Ship right-sized media
Use responsive sizes, modern formats, explicit dimensions, and eager loading for likely LCP media.
Protect interactions
Keep handlers short, schedule non-urgent work, and reduce layout/paint before the next frame.
Measurement Tools
Tool choice depends on whether you are discovering production pain or debugging a local cause.
| Tool | Use it for |
|---|---|
| Chrome DevTools Performance | Debug main-thread work, rendering, long tasks, layout, and interaction traces. |
| Lighthouse | Run repeatable lab audits and find broad loading, accessibility, and best-practice issues. |
| PageSpeed Insights / CrUX | Check real-user Core Web Vitals when field data is available. |
| WebPageTest | Inspect waterfalls, filmstrips, connection behavior, and resource priority in detail. |
| RUM | Track production performance by route, release, device class, geography, and user segment. |
Investigation Workflow
A reliable workflow separates field discovery from local debugging. Field data tells you whether real users are affected. Lab tools tell you why a reproducible case is slow.
- Segment the regression by route, device class, connection, geography, and release. Average scores hide the users who are actually hurt.
- Identify the primary symptom: loading delay, interaction delay, layout instability, memory pressure, or animation jank.
- Capture the right artifact: waterfall for loading, performance trace for main-thread work, bundle report for JavaScript weight, and field metrics for release impact.
- Apply one targeted change and re-measure. Multiple simultaneous fixes make attribution weak.
- Add a guardrail: bundle budget, image budget, Lighthouse CI check, performance test, or real-user alert tied to the metric that regressed.
Common Optimization Mistakes
- Lazy-loading the likely LCP image.
- Preloading many resources until nothing is actually prioritized.
- Splitting tiny modules into extra requests with no measurable win.
- Optimizing desktop traces when the field regression is mobile-only.
- Replacing simple CSS with a large animation or UI library for a small effect.
- Chasing a lab score while real-user monitoring shows a different bottleneck.
Interview Framing
A strong answer starts with measurement and narrows to the bottleneck. Avoid listing disconnected tricks.
Senior answer pattern
I would start with real-user metrics and identify whether the route is limited by loading, JavaScript, rendering, or media. Then I would reproduce the bottleneck in a trace, apply the smallest targeted fix, and verify both in lab tools and field data. For example, LCP might require earlier resource discovery, INP might require reducing main-thread work, and CLS might require reserving layout space.