This episode covers HTTP caching and stale-while-revalidate, code splitting with bundle analysis, prefetching data and assets, and monitoring network performance. You'll build an app that not only works but also feels fast.

The speed of a React app is largely determined by the network: how many bytes are downloaded, how many requests are sent, and how often. Episode 14 covers the networking strategies that make an app feel instant even with many resources.
We start with HTTP caching and the stale-while-revalidate pattern, then code splitting with bundle analysis to see the real bundle size, prefetching data and assets, and end with how to monitor network performance so regressions are caught early.
The best caching starts at the HTTP response. The Cache-Control header tells the browser how long to store the response:
Cache-Control: public, max-age=3600, stale-while-revalidate=86400max-age=3600 stores the response for one hour, and stale-while-revalidate=86400 lets the browser use the stale version for one day while refreshing in the background. The result: the second request feels instant because it's served from the local cache.
The same pattern is applied at the JavaScript level by SWR — the library's name is indeed taken from this strategy:
import useSWR from "swr"
function Profil() {
const { data } = useSWR("/api/profil", fetcher, {
revalidateOnFocus: true,
refreshInterval: 60000,
})
if (!data) return <p>Memuat...</p>
return <p>Halo, {data.nama}</p>
}useSWR("/api/profil", fetcher) immediately shows cached data while fetching fresh data in the background. refreshInterval: 60000 updates every minute without the user noticing.
Before optimizing, measure first. Vite provides a build mode with a visual report:
npm install -D rollup-plugin-visualizer
npx vite buildRun the build with the visualizer plugin to produce an interactive report mapping each module's size. You'll immediately see which modules bloat the bundle — for example, a large library only used by one feature.
Large bundles often come from dependencies that shouldn't be in the initial chunk. Split heavy libraries into separate chunks in vite.config.js:
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
charts: ["recharts"],
},
},
},
},
}manualChunks separates react/react-dom and recharts into their own chunks so the browser can cache them separately. Libraries that rarely change don't need to be re-downloaded when your app code changes.
Combine route-based code splitting (episode 9) with lazy for heavy components like editors or charts. Only load them when actually needed — reducing the initial load.
Pages that need to load important assets earlier can use resource hints:
<link rel="preconnect" href="https://api.example.com" />
<link rel="preload" href="/hero.png" as="image" /><link rel="preconnect"> opens the connection to the API domain earlier, cutting handshake latency. <link rel="preload"> downloads the hero image before it's needed, so the image appears instantly on render.
As in episode 13, use prefetchQuery when the user heads to the next page. Combine it with onMouseEnter on links and useEffect on routes to prepare data in the background.
Start with the tools you already have: the Network panel in DevTools shows the number of requests, transfer size, and the waterfall of each resource. For ongoing metrics, monitor the Core Web Vitals:
npm run devOpen DevTools, the Network tab, then reload the page with throttling set to Slow 4G. Note how many bytes are downloaded before optimization, then compare after splitting the bundle and adding caching — episode 22 will automate this monitoring in production.
Episode 14 made your app fast on the network: HTTP caching with stale-while-revalidate, code splitting with bundle analysis, prefetching data and assets, and performance monitoring with Web Vitals and the Network panel.
Key takeaways:
Cache-Control with max-age and stale-while-revalidate cuts requests.manualChunks separates vendors so they can be cached separately.In the next episode, episode 15, we'll cover performance optimization — profiling with React DevTools and browser tools, memoization with useMemo and useCallback, virtualization for long lists, and patterns for avoiding unnecessary renders. Let's make your React as lean as possible.