Skip to content

Instantly share code, notes, and snippets.

@everget
Last active December 18, 2024 15:28
Show Gist options
  • Save everget/52041712ec5a14627031d553a2510cf4 to your computer and use it in GitHub Desktop.
Save everget/52041712ec5a14627031d553a2510cf4 to your computer and use it in GitHub Desktop.
Pros and Cons of JS default exports

Pros of Default Exports:

  1. Simple Syntax for Single Exports:

    • For files that export only one function, class, or object, default exports offer a simple and concise syntax: export default MyClass or export default myFunction().
    • This allows direct, clean import statements: import MyClass from './MyClass', reducing verbosity when working with a single, obvious export.
  2. Convenient for Dynamic Imports:

    • Default exports integrate relatively cleanly with import() for dynamic imports, although there are naming challenges (see cons).

Cons and Downsides of Default Exports:

  1. Lack of Clear Intention:

    • Problem: Default exports can be confusing as they don't provide any information about what is being exported. The importer may not know whether it's a class, function, or object.
    • Effect: Developers must manually inspect the file or rely on documentation/IDE to understand what the default export represents. This leads to increased cognitive load or reliance on tooling for autocompletion and intellisense.
  2. Naming Inconsistencies:

    • Problem: With default exports, the importer can choose any name for the imported module, e.g., import Foo from './Bar'.
    • Effect: This leads to potential inconsistency across the codebase, where the same module is imported under different names, making it harder to search and refactor code reliably.
  3. Worse Tooling Support:

    • Problem: IDEs and linters have a harder time providing intellisense and autocompletion for default exports because there's no fixed name associated with them.
    • Effect: Tools like ESLint or WebStorm have reduced ability to automatically fix imports or detect naming issues. Errors can occur at runtime if the default export is misspelled or misused without feedback from the IDE.
  4. Reduced Refactorability:

    • Problem: Refactoring default exports is more error-prone. Renaming a default export in the source file doesn’t automatically update the imports in other files.
    • Effect: Manually renaming default exports increases the risk of missing places where the export is used, leading to broken imports or incorrect usage.
  5. Tree Shaking Issues:

    • Problem: Default exports can inhibit efficient tree shaking, as bundlers like Webpack often import the entire module, including unused parts, leading to larger bundle sizes.
    • Effect: Performance may suffer due to the inclusion of dead code, which can be mitigated with named exports that allow better granularity in what is imported.
  6. CommonJS Interop Issues:

    • Problem: Importing default exports from CommonJS modules results in awkward syntax, e.g., const { default } = require('module').
    • Effect: This creates friction when mixing CommonJS and ES modules in legacy codebases.
  7. Discoverability and Autocompletion:

    • Problem: Default exports offer poor discoverability. When importing a module, it’s not clear whether it has a default export or what it exports, making it hard for developers to explore modules.
    • Effect: Named exports provide better discoverability with intellisense and autocompletion, offering developers a smoother coding experience.
  8. Re-exporting Complexity:

    • Problem: Re-exporting a default export requires naming it explicitly: export { default as Foo } from './foo'. This is more verbose compared to named exports.
    • Effect: It adds unnecessary complexity when organizing code in larger projects, especially in module index files.
  9. Dynamic Imports Complications:

    • Problem: When using dynamic imports, the default export is exposed as default, leading to awkward syntax: MyModule.default.
    • Effect: Named exports avoid this, making dynamic imports more intuitive: const { HighCharts } = await import('...');.
  10. Potential for Typos:

  • Problem: Because default exports allow arbitrary import names, typos or inconsistent capitalization are easier to introduce, e.g., import foo from './foo' vs. import Foo from './foo'.
  • Effect: These subtle errors can go unnoticed without static analysis tools and lead to bugs.

Summary of Downsides:

  • Inconsistent naming: Importing defaults can lead to ad-hoc, inconsistent names across a codebase.
  • Poor tooling support: Default exports limit the ability of tools to provide autocompletion, refactoring, and error detection.
  • Tree-shaking inefficiency: Default exports can bloat bundle sizes by inhibiting proper tree shaking.
  • Refactoring difficulty: Renaming default exports is less straightforward and prone to errors compared to named exports.
  • Poor discoverability: Lack of intellisense and transparency about what the default export is without manual inspection or documentation.
  • Interop friction: Especially with CommonJS modules, default exports add unnecessary friction in interop situations.
  • Cognitive overhead: Increases cognitive load for developers when guessing or searching for what is being imported.

Recommendation:

To avoid these pitfalls, prefer named exports as they:

  • Improve readability and maintainability by making explicit what is exported.
  • Facilitate tooling support for refactoring, autocompletion, and error checking.
  • Ensure tree shaking is effective and reduce bundle sizes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment