import/order

Enforce a consistent order of imports, by group and optionally by name

fixable

Also called import-x/order

When people add imports wherever there's room, you can't see at a glance what a file depends on, and two branches that add an import in the same spot conflict. This rule sorts imports into groups by where they come from.

The default order is Node.js built-ins, packages, parent folders (../), the same folder (./), then the folder's own index (. or ./). Inside a group any order is fine, unless you set alphabetize.

  • The rule checks import and top-level const x = require(). All require() calls go after all import statements.
  • import "./app.css" and other imports without a name are skipped, because moving them can change what the code does.
  • bun and bun:test count as built-ins. With an ESLint config they count as packages, unless settings["import/core-modules"] lists them.

--fix moves the imports, unless there's other code between them.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"import/order" = "error" # or "warn"

Every option, set to its default. The ones that are commented out are unset by default.

bunfig.toml
[lint.rules]
"import/order" = ["error", {
  groups = ["builtin", "external", "parent", "sibling", "index"],
  # "ignore", "always", "always-and-inside-groups" or "never"
  newlines-between = "ignore",
  # alphabetize = <object>,
  named = false,
  pathGroups = [], # list of objects
  pathGroupsExcludedImportTypes = ["builtin", "external", "object"],
  distinctGroup = true,
  sortTypesGroup = false,
  # newlines-between-types = <string>,
  consolidateIslands = "never", # "inside-groups" or "never"
  warnOnUnassignedImports = false,
}]

Examples#

example.js2 problems
import { formatDate } from "./format.js";
import express from "express";
import fs from "node:fs";
example.jsNo problems
import fs from "node:fs";
import { serve } from "bun";
import express from "express";
import { config } from "../config.js";
import { formatDate } from "./format.js";
import { routes } from "./";

Options#

OptionDefaultWhat it does
groups["builtin", "external", "parent", "sibling", "index"]The order of the groups. A nested array makes several groups count as one. Groups you leave out come last, together.
newlines-between"ignore""always": one or more empty lines between groups, none inside. "always-and-inside-groups": the same, but empty lines inside a group are fine. "never": no empty lines at all.
alphabetize{ order: "ignore" }Sort each group by path. order: "asc" or "desc". caseInsensitive: false by default. orderImportKind: for two imports of the same path, "asc" puts the import type first and "desc" puts it last.
namedfalseAlso sort the names inside { }, by alphabetize.order. true, or an object with enabled for all and import, export, require, cjsExports for each kind. Its types key says where inline type names go: "mixed", "types-first" or "types-last".
pathGroups[]Puts paths that match a glob into a group: { pattern, group, position }. position is "before" or "after" the rest of the group. patternOptions takes glob options such as nocase and matchBase.
pathGroupsExcludedImportTypes["builtin", "external", "object"]Kinds of imports that pathGroups leaves alone.
distinctGrouptrueFor newlines-between, treat a path group with a position as a separate group.
sortTypesGroupfalseOrder the imports in the "type" group by groups too.
newlines-between-typessame as newlines-betweenWith sortTypesGroup, the empty lines around and between import type statements.
consolidateIslands"never""inside-groups", with "always-and-inside-groups": an empty line around each multi-line import, none between single-line imports of a group.
warnOnUnassignedImportsfalseAlso check imports without a name. --fix doesn't move them.

You can put four more groups in groups:

  • "internal": a path that isn't relative and resolves to a file in your project.
  • "unknown": a path that doesn't fit any other group.
  • "object": import log = console.log in TypeScript.
  • "type": import type.

newlines-between#

With "always", this is reported:

example.jswith { "newlines-between": "always" }1 problem
import fs from "node:fs";
import express from "express";

And this isn't:

example.jswith { "newlines-between": "always" }No problems
import fs from "node:fs";

import express from "express";

alphabetize and named#

bunfig.toml
[lint.rules]
"import/order" = ["error", {
  named = true,
  alphabetize = { order = "asc", caseInsensitive = true },
}]
example.jswith { "named": true, "alphabetize": { "order": "asc", "caseInsensitive": true } }2 problems
import { writeFile, readFile } from "node:fs/promises";
import express from "express";
import chalk from "chalk";
example.jswith { "named": true, "alphabetize": { "order": "asc", "caseInsensitive": true } }No problems
import { readFile, writeFile } from "node:fs/promises";
import chalk from "chalk";
import express from "express";

pathGroups#

Use it for path aliases such as @/, which otherwise count as "unknown":

bunfig.toml
[lint.rules]
"import/order" = ["error", {
  groups = ["builtin", "external", "internal", "parent", "sibling", "index"],
  pathGroups = [{ pattern = "@/**", group = "internal" }],
}]
example.jswith { "groups": ["builtin", "external", "internal", "parent", "sibling", "index"], "pathGroups": [{ "pattern": "@/**", "group": "internal" }] }1 problem
import { config } from "../config.js";
import { Button } from "@/components/button";
example.jswith { "groups": ["builtin", "external", "internal", "parent", "sibling", "index"], "pathGroups": [{ "pattern": "@/**", "group": "internal" }] }No problems
import { Button } from "@/components/button";
import { config } from "../config.js";

An alias that looks like a package name, such as @app/button, counts as "external", and pathGroups skips it. Set pathGroupsExcludedImportTypes to ["builtin"] to include it.

The "type" group#

With "type" in groups, every import type goes in that spot, no matter where it imports from:

example.tswith { "groups": ["builtin", "external", "parent", "sibling", "index", "type"] }1 problem
import type { Server } from "node:http";
import { formatDate } from "./format.ts";
example.tswith { "groups": ["builtin", "external", "parent", "sibling", "index", "type"] }No problems
import { formatDate } from "./format.ts";
import type { Server } from "node:http";