Front-End Performance Optimisation
Performance matters more than developers often realize. Every 100ms increase in page load time reduces conversions. Users on slow connections abandon slow sites. Mobile users feel poor performance acutely. Performance isn't a nice-to-have—it directly impacts your business metrics.
Front-end performance involves three main concerns: bundle size (how much JavaScript/CSS is shipped), loading performance (how fast initial content appears), and runtime performance (how responsive the page feels during interaction).
Bundle Size: The Foundation
Bundle size is the total amount of JavaScript and CSS sent to the browser. Every byte must be downloaded and parsed. Larger bundles are slower, especially on mobile connections.
Measure your bundle size. Use webpack-bundle-analyzer or similar tools to see what's in your bundle. Common culprits: large dependencies, importing entire libraries when you only need a function, duplicate dependencies due to version conflicts.
Small improvements compound. Removing an unused 50KB dependency saves 50KB on every page load. With millions of page loads, that's significant.
Code Splitting: Load What You Need
Most applications don't need all their code on the initial page load. User dashboard, admin panel, settings page—only some users visit these. Code splitting divides your bundle into chunks and loads them on demand.
In React, use React.lazy and Suspense to code split:
const Dashboard = React.lazy(() => import('./Dashboard'))
<Suspense fallback={<Spinner />}>
<Dashboard />
</Suspense>
The Dashboard component's code is loaded only when it's rendered. Users who never visit the dashboard never download that code.
Route-based code splitting is the easiest to implement: split at route boundaries. Most users visit only a few routes, so they never download code for routes they don't visit.
Tree Shaking: Removing Dead Code
Build tools like webpack can remove unused code if you write it in a way they understand. This is tree shaking. It only works for ES6 modules with static imports.
If you import a large library but only use one function, tree shaking should remove the rest. However, library design matters. If the library isn't designed for tree shaking (uses dynamic requires, doesn't provide ES6 exports), dead code remains.
When choosing dependencies, prefer libraries that support tree shaking. Check the package.json for "sideEffects": false (tells bundlers the library can be fully tree-shaken) and that it exports ES6 modules.
Image Optimisation
Images often represent the bulk of page weight. Optimizing images has outsized impact on performance.
- Use next/image or similar: Automatically optimizes, resizes for each device, lazy loads.
- Modern formats: WebP is significantly smaller than PNG/JPEG. Serve WebP to browsers that support it, fall back to JPEG.
- Proper sizing: Don't serve a 2000x2000 image to display at 200x200. It's wasteful.
- Lazy loading: Images below the fold (not visible initially) should lazy load. The user won't see them immediately, so why load them?
- Compression: Use image compression tools. A 2MB PNG can often be compressed to 200KB with no visible quality loss.
Core Web Vitals: User-Centric Metrics
Google's Core Web Vitals are metrics that measure user experience. They affect SEO rankings and correlate with conversion rates. Understanding them helps prioritize optimization efforts.
Largest Contentful Paint (LCP)
LCP measures when the largest piece of content appears on screen. Aim for under 2.5 seconds. For most sites, this is the hero image or main heading.
Improve LCP: optimize your server response time, don't block rendering with JavaScript or CSS, preload critical resources, defer non-critical resources.
Interaction to Next Paint (INP)
INP measures how responsive the page feels during interaction. When the user clicks a button or types in a field, how long until the page responds? Aim for under 200ms.
Improve INP: break up long JavaScript tasks into smaller chunks (use setTimeout to yield to the browser), avoid excessive DOM updates, use requestAnimationFrame for animations.
Cumulative Layout Shift (CLS)
CLS measures how stable the layout is. If content moves around unexpectedly (an ad loads, pushing content down), it's jarring. Aim for under 0.1.
Improve CLS: reserve space for images and ads before they load, avoid inserting content above existing content, use transforms for animations instead of changing dimensions.
Caching: Reusing What You've Already Loaded
Browser caching stores resources locally so they don't need to be downloaded again. Set Cache-Control headers on static assets: images, CSS, JavaScript should cache for days or longer. HTML should cache minimally or not at all (it changes more frequently).
CDN caching stores static assets on servers around the world. Requests serve from the geographically closest server, reducing latency.
Service workers enable offline functionality and advanced caching strategies. For most projects, browser and CDN caching are sufficient.
What Not to Over-Optimize
Premature optimization is waste. Before optimizing, measure. Use Lighthouse, WebPageTest, or your real user monitoring data to identify actual bottlenecks. Optimizing the wrong thing wastes time.
Common misguided optimizations:
- Micro-optimizing JavaScript: Saving 10ms in JavaScript won't help if your images are 5MB.
- Optimizing the fast path: If 90% of your load time is server response, optimizing your CSS won't help.
- Optimizing for your network: Your office internet is fast. Your users on 3G aren't. Optimize for their actual conditions, not yours.
- Optimizing for high-end devices: Your MacBook Pro is fast. Budget Android phones aren't. Test on slower devices.
Performance Monitoring
Synthetic monitoring (tools like Lighthouse) measures performance under controlled conditions. It's useful but doesn't capture real-world performance. Real user monitoring (RUM) measures actual user experience.
Collect Core Web Vitals from real users using web-vitals library or built-in APIs. Track performance over time. Alert on regressions.
Performance budgets help prevent regressions. Set a maximum bundle size, and fail the build if it's exceeded. This enforces discipline—every dependency addition must be justified.
The Business Case for Performance
Performance optimization often gets deprioritized in favor of new features. But slow sites lose users. Every 100ms delay reduces conversions by 1%. This compounds—a 2-second page is 20% slower than 1-second, and significantly fewer users convert.
Frame performance optimization as a user experience issue, not a technical one. "Our checkout is slow, causing cart abandonment" is more persuasive than "our bundle is 500KB."
Invest in performance tooling and monitoring. Make it easy to see performance impact of changes. Celebrate improvements. This culture shift—where performance is valued alongside new features—is the lasting benefit.