Learn ReactJS - Networking Performance
Episode 14 of 24

Learn ReactJS - Networking Performance

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

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.

HTTP Caching and Stale-While-Revalidate

Cache-Control on the Server Response

The best caching starts at the HTTP response. The Cache-Control header tells the browser how long to store the response:

Simple cache header
Cache-Control: public, max-age=3600, stale-while-revalidate=86400

max-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.

Stale-While-Revalidate at the Application Level

The same pattern is applied at the JavaScript level by SWR — the library's name is indeed taken from this strategy:

JSSWR with stale data
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.

Code Splitting, Bundle Analysis, and Lazy Loading

Analyzing Bundle Size

Before optimizing, measure first. Vite provides a build mode with a visual report:

Bundle analysis with Rollup Plugin Visualizer
npm install -D rollup-plugin-visualizer
npx vite build

Run 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.

Splitting Heavy Libraries

Large bundles often come from dependencies that shouldn't be in the initial chunk. Split heavy libraries into separate chunks in vite.config.js:

JSmanualChunks in Vite
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.

Lazy Loading for Everything That Isn't Urgent

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.

Prefetching Data and Assets

Resource Hints in index.html

Pages that need to load important assets earlier can use resource hints:

Preload and preconnect
<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.

Prefetch Data with queryClient

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.

Monitoring Network Performance

Web Vitals and the Network Panel

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:

  • LCP (Largest Contentful Paint): when the main content appears.
  • INP (Interaction to Next Paint): how responsive the app is to clicks.
  • CLS (Cumulative Layout Shift): how stable the layout is while loading.
Open the dev server and measure
npm run dev

Open 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.

Conclusion

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.
  • SWR applies the stale data strategy at the application level.
  • Measure the bundle first with a visualizer, then split heavy libraries.
  • manualChunks separates vendors so they can be cached separately.
  • Preconnect and preload speed up downloading important assets.
  • Monitor LCP, INP, and CLS to measure the real experience.

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.