| 1 | --- |
| 2 | title: Avoid Barrel File Imports |
| 3 | impact: CRITICAL |
| 4 | impactDescription: 200-800ms import cost, slow builds |
| 5 | tags: bundle, imports, tree-shaking, barrel-files, performance |
| 6 | --- |
| 7 | |
| 8 | ## Avoid Barrel File Imports |
| 9 | |
| 10 | Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`). |
| 11 | |
| 12 | Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts. |
| 13 | |
| 14 | **Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph. |
| 15 | |
| 16 | **Incorrect (imports entire library):** |
| 17 | |
| 18 | ```tsx |
| 19 | import { Check, X, Menu } from 'lucide-react' |
| 20 | // Loads 1,583 modules, takes ~2.8s extra in dev |
| 21 | // Runtime cost: 200-800ms on every cold start |
| 22 | |
| 23 | import { Button, TextField } from '@mui/material' |
| 24 | // Loads 2,225 modules, takes ~4.2s extra in dev |
| 25 | ``` |
| 26 | |
| 27 | **Correct - Next.js 13.5+ (recommended):** |
| 28 | |
| 29 | ```js |
| 30 | // next.config.js - automatically optimizes barrel imports at build time |
| 31 | module.exports = { |
| 32 | experimental: { |
| 33 | optimizePackageImports: ['lucide-react', '@mui/material'] |
| 34 | } |
| 35 | } |
| 36 | ``` |
| 37 | |
| 38 | ```tsx |
| 39 | // Keep the standard imports - Next.js transforms them to direct imports |
| 40 | import { Check, X, Menu } from 'lucide-react' |
| 41 | // Full TypeScript support, no manual path wrangling |
| 42 | ``` |
| 43 | |
| 44 | This is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost. |
| 45 | |
| 46 | **Correct - Direct imports (non-Next.js projects):** |
| 47 | |
| 48 | ```tsx |
| 49 | import Button from '@mui/material/Button' |
| 50 | import TextField from '@mui/material/TextField' |
| 51 | // Loads only what you use |
| 52 | ``` |
| 53 | |
| 54 | > **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports. |
| 55 | |
| 56 | These optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR. |
| 57 | |
| 58 | Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`. |
| 59 | |
| 60 | Reference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js) |
| 61 |