A logo showing the text blog.marcnuri.com
Español
Home»JavaScript»What are Barrel Exports in JavaScript and TypeScript?

Recent Posts

  • 2025 Year in Review: The Year of AI
  • Synology DS224+: How to upgrade hard drives in RAID 1
  • Fabric8 Kubernetes Client 7.5 is now available!
  • Boosting My Developer Productivity with AI in 2025
  • Black Box vs White Box Testing: When to Use Each Approach

Categories

  • Artificial Intelligence
  • Backend Development
  • Cloud Native
  • Engineering Insights
  • Frontend Development
  • JavaScript
  • Legacy
  • Operations
  • Personal
  • Pet projects
  • Quality Engineering
  • Tools

Archives

  • January 2026
  • December 2025
  • October 2025
  • September 2025
  • July 2025
  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • August 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • October 2019
  • September 2019
  • July 2019
  • March 2019
  • November 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • December 2017
  • October 2017
  • August 2017
  • July 2017
  • January 2017
  • December 2015
  • November 2015
  • December 2014
  • November 2014
  • October 2014
  • March 2014
  • February 2011
  • November 2008
  • June 2008
  • May 2008
  • April 2008
  • January 2008
  • November 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007

What are Barrel Exports in JavaScript and TypeScript?

2020-03-21 in JavaScript tagged JavaScript / Node / npm / React by Marc Nuri | Last updated: 2026-02-08
Versión en Español

Introduction

If you've worked on a JavaScript or TypeScript project with multiple modules, you've likely encountered long and repetitive import statements. As projects grow, managing these imports becomes increasingly tedious. Barrel exports offer a pattern that helps keep your codebase clean and organized.

In this post, I'll explain what barrel exports are, how to create them, and when you should (or shouldn't) use them.

What is a Barrel Export?

A barrel is a file that re-exports modules from other files, consolidating them into a single entry point. These files are typically named index.js or index.ts.

The term "barrel" comes from the idea of "rolling up" exports from multiple modules into one convenient location, like items stored in a barrel. The Angular team popularized this term in their style guides, and it has since become standard vocabulary across the JavaScript ecosystem.

Note

Barrel exports are a convention, not a language feature. JavaScript and TypeScript don't treat index.js files specially at the language level. The magic happens in module bundlers and Node.js's module resolution algorithm, which automatically look for index.js when you import a directory.

Here's what a project looks like before and after adding a barrel:

Before (no barrel):

src/
  components/
    Button.js
    Card.js
    Modal.js
    Spinner.js
  app.js          ← imports from each file individually

After (with barrel):

src/
  components/
    Button.js
    Card.js
    Modal.js
    Spinner.js
    index.js      ← the barrel file
  app.js          ← imports from the directory

Creating a Barrel Export

Without a barrel, importing components requires specifying each file path explicitly:

app.js (without barrel)
import {Button} from './components/Button';
import {Card} from './components/Card';
import {Modal} from './components/Modal';
import {Spinner} from './components/Spinner';

To create a barrel, add an index.js file in the components directory that re-exports everything:

src/components/index.js
export {Button} from './Button';
export {Card} from './Card';
export {Modal} from './Modal';
export {Spinner} from './Spinner';

Now you can import everything from a single location:

app.js (with barrel)
import {Button, Card, Modal, Spinner} from './components';

Real-World Examples

I use barrel exports extensively in my open-source projects. Here are some examples from real codebases.

YAKD - Kubernetes Dashboard

YAKD is a Kubernetes dashboard I maintain. The project uses barrel exports to organize its UI React components into a clean, importable structure.

The main components barrel re-exports individual components and nested barrels:

YAKD: src/components/index.js
export * from './popup';
export * from './icons';
export {Age} from './Age';
export {Alert} from './Alert';
export {Card} from './Card';
export {DonutChart} from './DonutChart';
export {Dropdown} from './Dropdown';
export {FilterBar} from './FilterBar';
export {Form} from './Form';
export {Icon} from './Icon';
export {Link} from './Link';
export {Modal} from './Modal';
export {PopupMenu} from './PopupMenu';
export {ResourceListV2} from './ResourceListV2';
export {Spinner} from './Spinner';
export {Table} from './Table';
export {Tooltip} from './Tooltip';

Notice how this barrel also re-exports from nested barrels (./popup, ./icons), creating a hierarchical structure.

The icons barrel groups over 20 icon components, making them easy to import together:

YAKD: src/components/icons/index.jsx
export {ClusterRoleIcon} from './ClusterRoleIcon';
export {ConfigMapIcon} from './ConfigMapIcon';
export {CronJobIcon} from './CronJobIcon';
export {DaemonSetIcon} from './DaemonSetIcon';
export {DeploymentIcon} from './DeploymentIcon';
export {IngressIcon} from './IngressIcon';
export {JobIcon} from './JobIcon';
export {KubernetesIcon} from './KubernetesIcon';
export {NamespaceIcon} from './NamespaceIcon';
export {NodeIcon} from './NodeIcon';
export {PodIcon} from './PodIcon';
export {SecretIcon} from './SecretIcon';
export {ServiceIcon} from './ServiceIcon';
// ... and more

ElectronIM

ElectronIM is an Electron-based instant messaging client. Its barrel files organize both components and shared utilities, demonstrating how barrels can consolidate different types of exports.

The components barrel re-exports Preact hooks alongside UI components:

ElectronIM: src/components/index.mjs
export {APP_EVENTS, CLOSE_BUTTON_BEHAVIORS, ELECTRONIM_VERSION} from '../../bundles/constants.mjs';
export {createRef, html, render, useLayoutEffect, useReducer, useState} from '../../bundles/preact.mjs';

export {Button} from './button.mjs';
export {Card} from './card.mjs';
export {Checkbox} from './checkbox.mjs';
export {Icon} from './icon.mjs';
export {IconButton} from './icon-button.mjs';
export {Logo} from './electronim.mjs';
export {Menu} from './menu.mjs';
export {NavigationRail} from './navigation-rail.mjs';
export {Select} from './select.mjs';
export {Switch} from './switch.mjs';
export {TextField} from './text-field.mjs';
export {TopAppBar} from './top-app-bar.mjs';

This approach provides a single import point for everything a component might need: UI primitives, hooks, and application constants.

Pros of Barrel Exports

Simplified imports

Barrels reduce import clutter by consolidating multiple imports into one. This becomes especially helpful in UI-heavy codebases where components frequently import from the same directories.

Encapsulation

Barrels hide your internal directory structure from consumers. You can reorganize, rename, or split files without affecting external imports.

Clear public API

By explicitly listing exports, you define what's public and what's internal. Components not exported from the barrel remain implicitly private to the module.

Easier refactoring

When you move a file, you only need to update the barrel, not every file that imports it.

Named exports preserve component names

Unlike export default, barrel exports encourage named exports, which maintain consistent naming across your codebase:

Named exports vs default exports
// With default exports - names can vary unpredictably
import Button from './components/Button';
import Btn from './components/Button';      // Same component, different name!
import MyButton from './components/Button'; // Confusing

// With barrel exports using named exports - consistent naming
import {Button} from './components';
// "Button" is always "Button" everywhere

This consistency makes your code more searchable and self-documenting.

Cons of Barrel Exports

Performance impact

When you import from a barrel, bundlers and tools may process the entire module, including dependencies you don't use. This can slow down builds and tests significantly.

Warning

Atlassian reported a 75% reduction in build times after removing barrel files from their Jira frontend.

Circular dependency risk

Barrels can create circular imports. If tab-panel.js imports from index.js, and index.js re-exports from tab-panel.js, you have a cycle.

Caution

JavaScript handles circular dependencies somewhat gracefully, but bundlers may crash with obscure errors.

IDE navigation

"Go to definition" might take you to the barrel file instead of the actual implementation, adding an extra step when navigating code.

Tree-shaking challenges

Older bundlers and configurations struggle to eliminate unused exports from barrels, potentially increasing bundle size. This affects Webpack 4, older Rollup configurations without proper tree-shaking plugins, and Jest transformations that don't respect ES module semantics.

When to Use Barrel Exports

Use them for:

  • Library entry points: Libraries need a single entry point for package.json's "main" field
  • Component libraries: When you have a stable set of components frequently imported together
  • Icon libraries: Perfect for grouping many similar small modules
  • Grouping related functionality: Redux actions, API clients, utility functions

Avoid them in:

  • Large application codebases: Where build performance is critical
  • Directories with many modules: When only a few modules get imported at once
  • Performance-sensitive applications: Where every millisecond of build time matters

Best Practices

1. Prefer named exports over wildcards

Tip

Use export {Foo} from './Foo' instead of export * from './Foo'.

Wildcard re-exports (export *) have several downsides:

  • Accidental exports: Internal helpers or debug utilities can leak into your public API
  • Naming collisions: Two files exporting the same name cause silent overwrites
  • Circular dependency risk: Wildcards make it harder to trace what gets imported where
  • Worse IDE support: Autocomplete can't show you what's available without evaluating the entire module graph

Named exports make your barrel's public API explicit and intentional.

2. Keep barrels focused

Don't create mega-barrels that export hundreds of items. Split large directories into sub-barrels with clear boundaries.

3. Use ESLint plugins

Tools like eslint-plugin-barrel-files can enforce consistent patterns and catch potential issues early.

4. Consider granular entry points for libraries

Instead of one massive barrel, provide multiple smaller entry points (e.g., my-lib/components, my-lib/utils). This gives consumers more control over what they import.

5. Watch for circular dependencies

Be mindful when modules in the same directory import from each other. If file A imports from the barrel and the barrel re-exports A, you have a cycle.

Barrels and TypeScript

TypeScript works well with barrel exports, but there are a few quirks to keep in mind.

Incremental compilation in watch mode

Barrel files can sometimes confuse TypeScript's incremental compiler during tsc --watch or when using ts-node. When you modify a file that's re-exported through a barrel, TypeScript may not always detect the change correctly. If you notice stale types during development, a full rebuild usually resolves the issue.

Path aliases and barrels

TypeScript's paths option in tsconfig.json pairs nicely with barrels. You can create clean import paths that point to your barrel files:

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@components/*": ["src/components/*"],
      "@components": ["src/components/index.ts"]
    }
  }
}

This lets you write import {Button} from '@components' instead of relative paths.

Avoid duplicate type names

Don't barrel-export types with the same name from multiple files. Unlike runtime values that overwrite silently, duplicate type exports cause TypeScript compilation errors:

Duplicate type export error
// src/api/types.ts
export interface Response { /* ... */ }

// src/http/types.ts
export interface Response { /* ... */ }

// src/index.ts
export * from './api/types';
export * from './http/types'; // Error: Duplicate identifier 'Response'

Use named exports or rename conflicting types before re-exporting.

Conclusion

Barrel exports provide a useful pattern for organizing JavaScript and TypeScript projects. They simplify imports, encourage consistent naming, and create clear module boundaries.

However, they come with trade-offs. For large applications where build performance matters, you might want to avoid them or use them sparingly.

For libraries and component collections, barrels remain an excellent choice for providing a clean, user-friendly API.

Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Post navigation
Kubernetes Client for Java: Introducing YAKCGo Interfaces: Design Patterns & Best Practices
© 2007 - 2026 Marc Nuri