6.2 KiB
6.2 KiB
Frontend Optimization Examples
React and frontend performance optimizations with measurable Web Vitals improvements.
Example 1: Code Splitting
Problem: Large Bundle
// ❌ BEFORE: Single bundle - 1.2MB JavaScript, 4.5s load time
import { Dashboard } from './Dashboard';
import { Analytics } from './Analytics';
import { Settings } from './Settings';
import { Admin } from './Admin';
function App() {
return (
<Router>
<Route path="/" component={Dashboard} />
<Route path="/analytics" component={Analytics} />
<Route path="/settings" component={Settings} />
<Route path="/admin" component={Admin} />
</Router>
);
}
// Initial bundle: 1.2MB
// First Contentful Paint: 4.5s
Solution: Dynamic Imports
// ✅ AFTER: Code splitting - 200KB initial, 1.8s load time
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
const Analytics = lazy(() => import('./Analytics'));
const Settings = lazy(() => import('./Settings'));
const Admin = lazy(() => import('./Admin'));
function App() {
return (
<Router>
<Suspense fallback={<Loading />}>
<Route path="/" component={Dashboard} />
<Route path="/analytics" component={Analytics} />
<Route path="/settings" component={Settings} />
<Route path="/admin" component={Admin} />
</Suspense>
</Router>
);
}
// Initial bundle: 200KB (6x smaller)
// First Contentful Paint: 1.8s (2.5x faster)
Metrics
| Implementation | Bundle Size | FCP | LCP |
|---|---|---|---|
| Single Bundle | 1.2 MB | 4.5s | 5.2s |
| Code Split | 200 KB | 1.8s | 2.1s |
| Improvement | 83% less | 2.5x | 2.5x |
Example 2: React Rendering Optimization
Problem: Unnecessary Re-renders
// ❌ BEFORE: Re-renders entire list on every update - 250ms
function ProductList({ products }) {
const [filter, setFilter] = useState('');
return (
<>
<input value={filter} onChange={e => setFilter(e.target.value)} />
{products.map(product => (
<ProductCard
key={product.id}
product={product}
onUpdate={handleUpdate}
/>
))}
</>
);
}
// Every keystroke: 250ms to re-render 100 items
Solution: Memoization
// ✅ AFTER: Memoized components - 15ms per update
const ProductCard = memo(({ product, onUpdate }) => {
return <div>{product.name}</div>;
});
function ProductList({ products }) {
const [filter, setFilter] = useState('');
const handleUpdate = useCallback((id, data) => {
// Update logic
}, []);
const filteredProducts = useMemo(() => {
return products.filter(p => p.name.includes(filter));
}, [products, filter]);
return (
<>
<input value={filter} onChange={e => setFilter(e.target.value)} />
{filteredProducts.map(product => (
<ProductCard
key={product.id}
product={product}
onUpdate={handleUpdate}
/>
))}
</>
);
}
// Every keystroke: 15ms (17x faster)
Example 3: Virtual Scrolling
Problem: Rendering Large Lists
// ❌ BEFORE: Render all 10,000 items - 8s initial render
function UserList({ users }) {
return (
<div>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
// 10,000 DOM nodes created
// Initial render: 8,000ms
// Memory: 450MB
Solution: react-window
// ✅ AFTER: Render only visible items - 180ms initial render
import { FixedSizeList } from 'react-window';
function UserList({ users }) {
const Row = ({ index, style }) => (
<div style={style}>
<UserCard user={users[index]} />
</div>
);
return (
<FixedSizeList
height={600}
itemCount={users.length}
itemSize={80}
width="100%"
>
{Row}
</FixedSizeList>
);
}
// ~15 DOM nodes created (only visible items)
// Initial render: 180ms (44x faster)
// Memory: 25MB (18x less)
Example 4: Image Optimization
Problem: Large Unoptimized Images
<!-- ❌ BEFORE: 4MB PNG, 3.5s load time -->
<img src="/images/hero.png" alt="Hero" />
Solution: Optimized Formats + Lazy Loading
<!-- ✅ AFTER: 180KB WebP, lazy loaded - 0.4s -->
<picture>
<source srcset="/images/hero-small.webp" media="(max-width: 640px)" />
<source srcset="/images/hero-medium.webp" media="(max-width: 1024px)" />
<source srcset="/images/hero-large.webp" media="(min-width: 1025px)" />
<img
src="/images/hero-large.webp"
alt="Hero"
loading="lazy"
decoding="async"
/>
</picture>
Metrics
| Implementation | File Size | Load Time | LCP Impact |
|---|---|---|---|
| PNG | 4 MB | 3.5s | 3.8s LCP |
| WebP + Lazy | 180 KB | 0.4s | 1.2s LCP |
| Improvement | 96% less | 8.8x | 3.2x |
Example 5: Tree Shaking
Problem: Importing Entire Library
// ❌ BEFORE: Imports entire lodash (72KB)
import _ from 'lodash';
const debounced = _.debounce(fn, 300);
const sorted = _.sortBy(arr, 'name');
// Bundle includes all 300+ lodash functions
// Added bundle size: 72KB
Solution: Import Specific Functions
// ✅ AFTER: Import only needed functions (4KB)
import debounce from 'lodash-es/debounce';
import sortBy from 'lodash-es/sortBy';
const debounced = debounce(fn, 300);
const sorted = sortBy(arr, 'name');
// Bundle includes only 2 functions
// Added bundle size: 4KB (18x smaller)
Summary
| Optimization | Before | After | Gain | Web Vital |
|---|---|---|---|---|
| Code Splitting | 1.2MB | 200KB | 6x | FCP, LCP |
| Memo + useCallback | 250ms | 15ms | 17x | FID |
| Virtual Scrolling | 8s | 180ms | 44x | LCP, CLS |
| Image Optimization | 4MB | 180KB | 22x | LCP |
| Tree Shaking | 72KB | 4KB | 18x | FCP |
Web Vitals Targets
- LCP (Largest Contentful Paint): <2.5s
- FID (First Input Delay): <100ms
- CLS (Cumulative Layout Shift): <0.1
Previous: Caching Optimization | Next: Backend Optimization | Index: Examples Index