optimization-techniques
title: Optimization Techniques sidebar_position: 1β
Optimization Techniques
Code splitting, tree shaking, bundle optimization, React rendering performance, and Core Web Vitals.
Question 1: Core Web Vitals Optimizationβ
Difficulty: π‘ Medium Frequency: βββββ Time: 10 minutes Companies: Google, Meta, Vercel, Netflix
Questionβ
What are Core Web Vitals and how do you optimize for each one?
Answerβ
Core Web Vitals are Google's three key user-centric metrics:
| Metric | Measures | Good | Needs Improvement | Poor |
|---|---|---|---|---|
| LCP (Largest Contentful Paint) | Loading performance | < 2.5s | 2.5β4s | > 4s |
| INP (Interaction to Next Paint) | Interactivity | < 200ms | 200β500ms | > 500ms |
| CLS (Cumulative Layout Shift) | Visual stability | < 0.1 | 0.1β0.25 | > 0.25 |
LCP optimization:
<!-- Preload LCP image -->
<link rel="preload" as="image" href="hero.webp" fetchpriority="high" />
<!-- Use modern formats -->
<picture>
<source srcset="hero.avif" type="image/avif" />
<source srcset="hero.webp" type="image/webp" />
<img src="hero.jpg" alt="Hero" width="1200" height="600" />
</picture>
<!-- Defer non-critical JS -->
<script src="bundle.js" defer></script>
INP optimization:
// Break up long tasks
async function processData(items) {
const chunkSize = 100;
for (let i = 0; i < items.length; i += chunkSize) {
await new Promise(resolve => setTimeout(resolve, 0)); // yield to main thread
items.slice(i, i + chunkSize).forEach(processItem);
}
}
// Use startTransition for non-urgent updates (React 18)
import { startTransition } from 'react';
function handleClick() {
startTransition(() => {
setExpensiveState(newValue);
});
}
CLS optimization:
<!-- Always reserve space for images -->
<img src="photo.jpg" width="800" height="600" alt="Product" />
<!-- Reserve space for dynamic content -->
<div style="min-height: 400px">
{chartData ? <Chart /> : <Skeleton />}
</div>
<!-- Prevent font flash -->
<style>
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2');
font-display: swap;
}
</style>
Resourcesβ
Question 2: JavaScript Bundle Size Reductionβ
Difficulty: π‘ Medium Frequency: βββββ Time: 8 minutes Companies: Meta, Google, Airbnb
Questionβ
How do you reduce JavaScript bundle size? Explain code splitting and tree shaking.
Answerβ
Three primary techniques:
Code Splittingβ
Break the monolithic bundle into route-based or feature-based chunks loaded on demand.
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Each page becomes a separate chunk
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Impact: Reducing initial bundle from 850 KB to 280 KB (67% reduction) cuts Time to Interactive by 55% on 3G.
Tree Shakingβ
Eliminate unused code by using ES module static imports.
// β Bad: Imports entire lodash (531 KB)
import _ from 'lodash';
const unique = _.uniq(array);
// β
Good: Import only what's needed (8 KB)
import uniq from 'lodash/uniq';
const unique = uniq(array);
// β Bad: moment.js (287 KB) for simple formatting
import moment from 'moment';
const formatted = moment(date).format('YYYY-MM-DD');
// β
Good: date-fns with tree shaking (2 KB)
import { format } from 'date-fns';
const formatted = format(date, 'yyyy-MM-dd');
Webpack Bundle Analysisβ
npm install --save-dev webpack-bundle-analyzer
// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
priority: 10,
},
common: {
minChunks: 2,
name: 'common',
priority: 5,
reuseExistingChunk: true,
},
},
},
},
plugins: [new BundleAnalyzerPlugin()],
};
Resourcesβ
Question 3: React Rendering Performanceβ
Difficulty: π΄ Hard Frequency: βββββ Time: 10 minutes Companies: Meta, Google, Netflix, Airbnb
Questionβ
How do you optimize React rendering performance? When should you use React.memo, useMemo, and useCallback?
Answerβ
Key optimization tools:
React.memo β Skip re-renders when props haven't changedβ
// Without memo: re-renders on every parent update
function ProductCard({ product }) {
return <div>{product.name} β ${product.price}</div>;
}
// With memo: skips re-render if props are shallowly equal
const ProductCard = React.memo(function ProductCard({ product }) {
return <div>{product.name} β ${product.price}</div>;
});
// Custom comparison for deep equality
const ProductCard = React.memo(
function ProductCard({ product }) {
return <div>{product.name}</div>;
},
(prevProps, nextProps) => prevProps.product.id === nextProps.product.id
);
useMemo β Cache expensive computationsβ
function Dashboard({ orders }) {
// β Bad: recalculates on every render
const summary = orders.reduce((acc, order) => ({
total: acc.total + order.amount,
count: acc.count + 1,
}), { total: 0, count: 0 });
// β
Good: recalculates only when orders changes
const summary = useMemo(() =>
orders.reduce((acc, order) => ({
total: acc.total + order.amount,
count: acc.count + 1,
}), { total: 0, count: 0 }),
[orders]
);
}
useCallback β Stable function references for memoized childrenβ
function ParentList({ items }) {
// β Bad: new function on every render β breaks React.memo on children
const handleDelete = (id) => deleteItem(id);
// β
Good: stable reference β React.memo on children can skip re-renders
const handleDelete = useCallback((id) => deleteItem(id), []);
return items.map(item => (
<MemoizedItem key={item.id} item={item} onDelete={handleDelete} />
));
}
Virtualization β Only render visible itemsβ
import { FixedSizeList } from 'react-window';
// β Bad: renders all 10,000 rows in the DOM
function SlowList({ items }) {
return items.map(item => <div key={item.id}>{item.name}</div>);
}
// β
Good: renders only ~15 visible rows at a time
function FastList({ items }) {
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>{items[index].name}</div>
)}
</FixedSizeList>
);
}
React 18 Concurrent Featuresβ
import { useTransition, useDeferredValue } from 'react';
// useTransition: Mark updates as non-urgent
function SearchBar() {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
setQuery(e.target.value); // urgent: update input immediately
startTransition(() => {
setSearchResults(search(e.target.value)); // non-urgent: can be interrupted
});
};
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
</>
);
}
// useDeferredValue: Defer expensive renders
function ProductList({ query }) {
const deferredQuery = useDeferredValue(query);
const products = useFilteredProducts(deferredQuery);
return products.map(p => <ProductCard key={p.id} product={p} />);
}
When to use eachβ
| Hook | Use when | Avoid when |
|---|---|---|
React.memo | Pure component with expensive render, stable props | Simple/cheap renders, props change frequently |
useMemo | Expensive computation, referential equality needed | Cheap calculations (adds overhead) |
useCallback | Function passed to memoized child | No memoized children (overhead without benefit) |
Resourcesβ
Content from maurya-sachin/Frontend-Master-Prep-Series