← All posts
Performance2 min readProject: Portfolio

A 32 MB JavaScript chunk: how a few icons broke my dev server

My dev server kept throwing ChunkLoadError timeouts. The root layout chunk had grown to 32 MB, and almost all of it came from importing a handful of icons.

#Next.js#Webpack#Bundle Size#Debugging

The problem

Random ChunkLoadError: Loading chunk app/layout failed (timeout) in development.

The result

Layout chunk went from 32 MB to 3.6 MB; the timeouts stopped.

Stack

Next.js 14Webpackreact-icons

The problem

From time to time, my local site crashed with this error:

Unhandled Runtime Error
ChunkLoadError: Loading chunk app/layout failed.
(timeout: http://localhost:3000/_next/static/chunks/app/layout.js)

app/layout is the root of every page, so when it fails, nothing renders.

The details

A timeout on a local file is suspicious. Localhost should be instant. So the first thing I checked was the size of the chunk itself:

FileSize
app/layout.js32 MB

Thirty-two megabytes for a layout with a header and a footer. Next, I measured which modules made up that file:

ModuleSize in chunk
react-icons/si14.4 MB
react-icons/fa65.5 MB
react-icons/fa4.4 MB
react-icons/io53.5 MB
react-icons/ci1.1 MB

About 29 MB was icons, and I only used one or two icons from each pack: LinkedIn, GitHub, WhatsApp, Gmail, Instagram and X in the footer, plus a menu icon in the header.

The reason: each react-icons pack is one big file containing every icon. In production, tree-shaking removes the unused ones. In development, there is no tree-shaking, so importing one icon pulls in the entire pack. Five packs, 29 MB.

The challenges

  • Production was fine. The bug only existed in development, where the bundler skips tree-shaking. Measuring the production bundle would have shown nothing wrong.
  • The error message pointed at the network, not at the bundle size.
  • Barrel optimization didn't help. Next.js can optimize "barrel" imports, but a react-icons pack isn't a barrel of small files: it's a single file, so there is nothing to split.
  • I didn't want visual changes. The icons had to look and behave exactly the same, including the size and className props.

The solution

I generated a small local Icons.tsx containing only the icons the site uses, copying their SVG data directly from the installed package, and kept using the library's tiny GenIcon helper:

import { GenIcon, IconBaseProps, IconTree } from "react-icons";

const FaGithubData: IconTree = { tag: "svg", attr: { viewBox: "0 0 496 512" }, child: [/* ... */] };
export const FaGithub = (props: IconBaseProps) => GenIcon(FaGithubData)(props);

Then every import changed from react-icons/fa6 to the local file. 11 icons, 11 KB instead of 29 MB.

BeforeAfter
app/layout.js (dev)32 MB3.6 MB

Lessons learned

  • Measure before guessing. "Timeout" sounded like a network issue; the real issue was a 32 MB file.
  • Dev and production bundles behave differently. Tree-shaking hides problems in production that still hurt your daily development.
  • Look at what a library ships, not just what you import. One named import can mean megabytes.
  • Fix the cause, not the timeout. Raising the chunk timeout would have hidden the problem and kept the slow dev server.

Facing a similar problem?

I help teams fix performance, backend and reliability issues in production.

Let's talk
Welcome to My WebSite please enjoy and have funWelcome to My WebSite please enjoy and have funWelcome to My WebSite please enjoy and have funWelcome to My WebSite please enjoy and have fun

Componentity

Thanks for visiting my portfolio. Every project here reflects my passion for building and learning. If you’re interested in working together, have a question, or just want to connect—don’t hesitate to reach out. I'm always open to meaningful conversations and new ideas.

© 2026 Zakariya Zouazou. All Rights Reserved.

Contact Us