DEV Community

Bosco Domingo
Bosco Domingo

Posted on • Edited on

JavaScript imports guide (Everything you need to know, in 5 minutes)

Default import

import process from "node:process";

process.env.MY_VAR
Enter fullscreen mode Exit fullscreen mode
  • Imports the module’s default export (if one exists).
  • Shorthand for import { default as X } from "Y".
  • If the default export is an object (for example the process object in Node.js), calling methods via that object (e.g., process.on(...)) uses the correct object as the this context.
  • Use this when the module exports a stateful object or one “main” entity.

Named import

import { env } from "node:process";

env.MY_VAR
Enter fullscreen mode Exit fullscreen mode
  • Brings only the explicitly exported property as a raw, static reference.
  • If you import a function or method via named import and then call it without its owning object, you may lose the original this binding (so this may be undefined).
  • Reduces bundle size (although tree-shakers are quite good at this, it will make compilation faster).
  • Still runs code in the imported module.
  • Usage: pure or stateless functions and constants, reducing import size.

Namespace import

import * as process from "node:process";

process.env.MY_VAR // undefined
process.default.env.MY_VAR // "All good!"
Enter fullscreen mode Exit fullscreen mode
  • Collects all exports into a single frozen module namespace object (properties are immutable).
  • Renames the default export to default. All others are kept the same.
  • Methods called via namespace.method() will still have namespace as the calling object (so this inside the method will refer to namespace, assuming the method uses this).
  • Usage: accessing all exports from a module without having to import each one.

Side-effect import

import "node:process"; // Add a comment to explain why that import is there
Enter fullscreen mode Exit fullscreen mode
  • Runs the module’s top-level code but imports nothing.
  • Usage: polyfills, patches, or setup code.

Mixed imports

import process, { env } from "node:process";
Enter fullscreen mode Exit fullscreen mode
  • Combine default and named imports in one statement.
  • Handy when you want both the live object and specific named bindings.

Type-only import (TypeScript)

import type { MyComponent } from "./MyComponent.tsx";

/** @see MyComponent */
const MyComponentLazy = React.lazy(() => import("./MyComponent.tsx"));
Enter fullscreen mode Exit fullscreen mode
  • Imports only type information, erased at runtime.
  • Guarantees no runtime code is included.
  • Usage: bringing type info, linking to lazily imported modules

JSON import (with import assertions)

import config from "./config.json" with { type: "json" };
Enter fullscreen mode Exit fullscreen mode
  • Imports a JSON file directly as a parsed object.
  • Errors if assertion is not passed
  • You get type-checking and autocomplete for free!
  • Used to be asserts instead of with, you may see that some places. The correct one is with

Rule of thumb:

  • Use default import for live objects that have methods with this.
  • Use named import when you need just a piece of the module.
  • Use namespace import only for modules with pure functions/values.
  • Use type import to keep type dependencies clean (TS only).
  • Use JSON import for configs/data files that should be parsed automatically.
  • Use side-effect import very sparingly, for things that must run once globally, and make sure to document it.

Top comments (0)