Why Barrel Files Slow Down Your Big React Codebase
Barrel (index.js) re-exports look tidy but add hidden runtime and build costs. Learn practical trade‑offs and alternatives for large React apps.

What a barrel file actually does
In a small demo you might write src/components/Button/index.tsx and then re‑export it from src/components/index.ts. The idea is you can later import {Button} from "@/components" and keep import statements short.
Under the hood a barrel is just a export * from "./Button"; statement. It adds an extra module to the dependency graph, which the bundler has to resolve, parse and include in the final bundle.
Runtime impact at scale
When your app has a few dozen components the extra module is negligible. Once you cross the 200‑plus component mark, every barrel adds a layer of indirection that the JavaScript engine must traverse at load time. The browser ends up executing something like:
// generated by the bundler for a barrel
export * from "./Button";
export * from "./Modal";
// ... dozens moreEach export * triggers a runtime loop that copies property descriptors. In Chrome’s V8 this can add a few milliseconds to the initial script evaluation, which compounds on slower devices.
Build‑time penalties
Tools like Webpack, Vite or Turbopack walk the module graph to perform tree‑shaking. A deep barrel hierarchy makes that walk O(n²) because the same file can be visited multiple times through different barrels. In a real project we saw build times jump from 12 s to 19 s after adding a src/ui/index.ts that re‑exported 120 components.
Even with aggressive caching, the incremental rebuild cost rises because any change inside the barrel forces the bundler to invalidate the whole subtree.
When barrels are actually useful
Don’t toss them outright. They shine in two scenarios:
- Public libraries. Consumers import from a single entry point, and the library is compiled once, so the extra indirection is a one‑time cost.
- Feature‑gated groups. If you have a folder that only a handful of pages import, a barrel keeps the page imports tidy without hurting the rest of the app.
In a monorepo you can also expose a barrel at the package boundary, not at every internal folder.
Practical alternatives for large apps
1. Explicit imports. Write import Button from "@/components/Button"; where you need it. It’s a bit longer but removes the extra module.
2. Index‑only for leaf modules. Keep an index.ts next to the component file, but don’t re‑export from a parent folder. This gives you the nice Button/index.tsx file‑structure without the global barrel.
3. Automated lint rule. Enforce a rule that flags export * barrels in directories with more than X files. Tools like ESLint can be configured to warn you.
Bottom line
Barrel files are a convenience, not a free performance win. In a large React codebase they add runtime indirection and make builds slower. Use them sparingly—prefer explicit imports or leaf‑level barrels, and reserve global re‑exports for library boundaries where the cost is paid once.