Loading...
Loading...
March 23, 2026
In building out the shared UI library for my recent monorepo project, I initially leaned on a standard industry pattern: “Barrel Files.” By using a central index.ts to re-export every component, I made my imports look clean and concise. However, I soon discovered that in a Next.js App Router environment, this “clean” look is a silent performance killer.
The issue arises from how modern JavaScript runtimes and bundlers like Turbopack process these aggregated exports. When a page imports even a single Button atom from a barrel file, the compiler is often forced to parse and evaluate the entire directory.
In my project, this led to massive “import fan-out.” A route that only required a simple button was silently pulling in heavy icon sets, date pickers, and complex utilities. The results were measurable: my IDE became sluggish as TypeScript struggled to resolve over 11,000 modules, and my initial page chunks were bloated with code that was never executed. Furthermore, this lack of clear system “edges” caused my AI assistants to hallucinate brittle relative paths, creating a constant loop of manual corrections.
I decided to liquidate the internal barrel files and implement a Strict Public API using Node.js Subpath Exports. Instead of a giant “catch-all” index, I configured the package.json of my shared UI package to define explicit entry points for my Atomic Design layers.
By mapping @repo/ui/atoms, @repo/ui/molecules, and @repo/ui/organisms directly in the export map, I forced the bundler to resolve only the specific files required for a given route. This move also allowed me to better enforce my project’s scss-module-bem standards—specifically my rule limiting SCSS nesting to a maximum of 3 levels. By isolating component imports, I could more easily audit specificity and prevent the “global leakage” that often plagues large-scale CSS architectures.
Standardizing on this architecture allowed me to leverage the full power of Vite 6 and Storybook 10 for an ultra-fast development cycle. The impact on my Next.js production build was immediate:
This shift moved the repository from “working but heavy” to a high-velocity environment where both human and AI developers can operate with 100% refactor-safety.In building out the shared UI library for my recent monorepo project, I initially leaned on a standard industry pattern: “Barrel Files.” By using a central index.ts to re-export every component, I made my imports look clean and concise. However, I soon discovered that in a Next.js App Router environment, this “clean” look is a silent performance killer.
The issue arises from how modern JavaScript runtimes and bundlers like Turbopack process these aggregated exports. When a page imports even a single Button atom from a barrel file, the compiler is often forced to parse and evaluate the entire directory.
In my project, this led to massive “import fan-out.” A route that only required a simple button was silently pulling in heavy icon sets, date pickers, and complex utilities. The results were measurable: my IDE became sluggish as TypeScript struggled to resolve over 11,000 modules, and my initial page chunks were bloated with code that was never executed. Furthermore, this lack of clear system “edges” caused my AI assistants to hallucinate brittle relative paths, creating a constant loop of manual corrections.
I decided to liquidate the internal barrel files and implement a Strict Public API using Node.js Subpath Exports. Instead of a giant “catch-all” index, I configured the package.json of my shared UI package to define explicit entry points for my Atomic Design layers.
By mapping @repo/ui/atoms, @repo/ui/molecules, and @repo/ui/organisms directly in the export map, I forced the bundler to resolve only the specific files required for a given route. This move also allowed me to better enforce my project’s scss-module-bem standards—specifically my rule limiting SCSS nesting to a maximum of 3 levels. By isolating component imports, I could more easily audit specificity and prevent the “global leakage” that often plagues large-scale CSS architectures.
Standardizing on this architecture allowed me to leverage the full power of Vite 6 and Storybook 10 for an ultra-fast development cycle. The impact on my Next.js production build was immediate:
This shift moved the repository from “working but heavy” to a high-velocity environment where both human and AI developers can operate with 100% refactor-safety.