Logo
Published on

Default Exports

How Default Exports Simplifies Module Structure

Default exports work best for modules with a single primary export - classes, main components, or primary functions. This pattern creates clean imports without curly braces and clearly communicates that a module has one main purpose. Teams using default exports report simpler import statements and better module clarity.

TL;DR

  • Export single main item with export default ClassName
  • Import without braces: import Component from './Component'
  • Perfect for classes, React components, and main functions
  • Enables renaming imports for better context
const defaultExport = class Database {
  connect() {
    return this
  }
}

The Single Responsibility Challenge

You're creating a module that serves one primary purpose - a database connection class, a React component, or a configuration object. While the module might have helper functions, there's clearly one main export that consumers care about. You need a clean way to indicate this primary export.

// database.js - Unclear main export
function connect(url) {
  console.log('Connecting to:', url)
  return { connected: true }
}
function disconnect() {
  console.log('Disconnecting')
  return { connected: false }
}
function query(sql) {
  console.log('Executing:', sql)
  return []
}
// Which is the main export?
const database = { connect, disconnect, query }
console.log('Database object:', database)

Default exports make the primary export obvious and enable cleaner import syntax:

// database.js - Clear default export (simulated)
class Database {
  constructor(url) {
    this.url = url
    this.connected = false
    console.log('Database instance created for:', url)
  }
  async connect() {
    console.log('Connecting to:', this.url)
    this.connected = true
    return this
  }
}
const defaultExport = Database // Export as default
console.log('Database class exported')

Best Practises

Use default exports when:

  • ✅ Module has one clear primary export (class, component)
  • ✅ Creating React components or Vue components
  • ✅ Exporting configuration objects or main functions
  • ✅ Want to allow import renaming for better context

Avoid when:

  • 🚩 Module exports multiple equally important functions
  • 🚩 Creating utility libraries with many functions
  • 🚩 Exporting constants that are better as named exports
  • 🚩 Need tree-shaking of individual functions

System Design Trade-offs

AspectDefault ExportNamed Export
Import SyntaxClean - no curly bracesExplicit - requires braces
RenamingEasy - rename during importComplex - requires 'as' keyword
Bundle AnalysisClear - single main exportDetailed - shows specific imports
RefactoringFlexible - rename without breakingRigid - exact name matching
IntentClear - this is THE exportNeutral - one of many exports
Tree ShakingAll or nothing approachFine-grained elimination

More Code Examples

❌ Multi-export confusion
// UserProfile.js - Unclear which export is main
function UserProfile(props) {
  console.log('Rendering profile for:', props.user.name)
  return {
    render: () => `<div>Profile: ${props.user.name}</div>`,
  }
}
function UserAvatar(props) {
  console.log('Rendering avatar for:', props.user.name)
  return {
    render: () => `<img src="${props.user.avatar}" />`,
  }
}
function UserActions(props) {
  console.log('Rendering actions for user:', props.user.id)
  return {
    render: () => '<div>Edit | Delete</div>',
  }
}
const USER_ROLES = {
  ADMIN: 'admin',
  USER: 'user',
  GUEST: 'guest',
}
// Consumer confused about main component
// import { UserProfile, UserAvatar, UserActions } from './UserProfile';
// Which one is the primary component?
const testUser = {
  id: 1,
  name: 'Alice',
  avatar: '/avatars/alice.jpg',
  role: 'user',
}
const profile = UserProfile({ user: testUser })
console.log('Rendered profile:', profile.render())
console.log('Multiple exports without clear hierarchy')
✅ Clear default priority
// UserProfile.js - Clear default with supporting exports (simulated)
const USER_ROLES = { ADMIN: 'admin', USER: 'user', GUEST: 'guest' }
function UserAvatar({ user }) {
  console.log('Rendering avatar for:', user.name)
  return {
    render: () => `<img src="${user.avatar}" alt="${user.name}" />`,
  }
}
function UserActions({ user }) {
  console.log('Rendering actions for user:', user.id)
  const canEdit = user.role !== USER_ROLES.GUEST
  return {
    render: () => (canEdit ? '<div>Edit | Delete</div>' : '<div>View Only</div>'),
  }
}
function UserProfile(props) {
  console.log('Rendering main profile component for:', props.user.name)
  const avatar = UserAvatar({ user: props.user })
  const actions = UserActions({ user: props.user })
  return {
    render: () => `<div class="user-profile">
      <h2>${props.user.name}</h2>
      ${avatar.render()}
      <p>Role: ${props.user.role}</p>
      ${actions.render()}
    </div>`,
  }
}
// Simulating exports
const defaultExport = UserProfile // Default export
const namedExports = { UserAvatar, UserActions, USER_ROLES }
// Consumer gets clean import syntax
const testUser = {
  id: 1,
  name: 'Alice',
  avatar: '/avatars/alice.jpg',
  role: USER_ROLES.USER,
}
const profileComponent = UserProfile({ user: testUser })
console.log('Main component rendered')
console.log('Default export makes primary component obvious')

Technical Trivia

The React Import Disaster of 2020: A large team was building a design system with hundreds of components. They used named exports for everything, leading to massive import statements like import { Button, Card, Modal, Input, Select, Dropdown, ... } from '@company/ui' with 30+ imports per file.

Why named-only exports failed: Developer productivity plummeted as import statements became unmanageable. Bundle analysis was impossible since every component was a named export. The main components weren't obvious among dozens of utilities and sub-components.

Default exports saved the day: Switching primary components to default exports created clean imports: import Button from '@company/ui/Button'. Bundle analysis became clear, main components were obvious, and import statements stayed readable even in complex applications.


Master Default Exports: Single Purpose Modules

Use default exports when your module has one clear primary purpose - a class, React component, or main function. This pattern communicates intent and simplifies imports. Combine with named exports for supporting utilities when needed. Avoid default exports for utility libraries where all functions are equally important.