organizeImports
Summary
Section titled “Summary”- Rule available since:
v1.0.0 - Diagnostic Category:
assist/source/organizeImports - This action is recommended.
- Sources:
- Inspired from
sort-imports - Inspired from
no-duplicate-imports - Inspired from
import/order
- Inspired from
How to enable in your editor
Section titled “How to enable in your editor”{ "editor.codeActionsOnSave": { "source.organizeImports.biome": "explicit", "source.fixAll.biome": "explicit" }}{ "code_actions_on_format": { "source.organizeImports.biome": true, "source.fixAll.biome": true }}source.organizeImports.biome How to configure
Section titled “How to configure”{ "assist": { "actions": { "source": { "organizeImports": "on" } } }}Description
Section titled “Description”Sorts imports and exports in your JavaScript and TypeScript files.
By default, imports and exports are sorted by “distance” from the current file:
- URLs such as
https://example.org. - Packages with a protocol such as
node:path,bun:test,jsr:@my?lib, ornpm:lib. - Packages such as
mylibor@my/lib. - Aliases: sources starting with
@/,#,~,$, or%. They usually are Node.js subpath imports or TypeScript path aliases. - Absolute and relative paths.
Imports and exports with the same distance are sorted using a
natural sort order
such that A < a < a9 < a10 < B < b
The action also merges imports and exports from the same source, sorts named specifiers and attributes using a natural sort order.
For example, the following code…
import sibling from "./file.js";import internal from "#alias";import fs from "fs";import { test } from "node:test";import path from "node:path";import { mock } from "node:test";import parent from "../parent.js";import scopedLibUsingJsr from "jsr:@scoped/lib";import data from "https://example.org";import { B, A, b, a10, a9 } from "lib";import scopedLib from "@scoped/lib";export { Y } from "dep";export * from "./inner.js";export { X } from "dep";…is sorted as follows:
import data from "https://example.org";import scopedLibUsingJsr from "jsr:@scoped/lib";import path from "node:path";import { mock, test } from "node:test";import scopedLib from "@scoped/lib";import fs from "fs";import { A, a9, a10, B, b } from "lib";import internal from "#alias";import parent from "../parent.js";import sibling from "./file.js";
export { X, Y } from "dep";export * from "./inner.js";Options
Section titled “Options”The action provides several options to customize how imports and exports are ordered:
groupsallows to group imports and exports before sorting them; It allows expressing custom order between imports or exports.identifierOrderallows changing how named specifiers and attributes are sorted
groups
Section titled “groups”You can customize how imports and exports are grouped using the groups option.
The option accepts an array of group matchers,
which in their simplest form are glob patterns or predefined group matchers.
Imports and exports that don’t match any group are automatically moved after all the groups.
Groups are always matched in order, so earlier matchers take priority.
To exclude some imports of a group, you can use an array of group matchers
with negated matchers, prefixed with !.
In the following example, we use the negated glob matcher !@myown/**,
to exclude @myown/package from the :PACKAGE: group.
With this configuration…
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ [ ":BUN:", ":NODE:" ], ":BLANK_LINE:", [ ":PACKAGE:", "!@myown/**" ], ":BLANK_LINE:", "@myown/**", ":BLANK_LINE:", [ ":ALIAS:", ":PATH:" ] ] } } } } }}…the following code…
import aliased from "@/components/Button";import lib from "lib";import path from "node:path";import sibling from "./file.js";import myown from "@myown/package";import scopedLib from "@scoped/lib";import fs from "fs";…is sorted as:
import fs from "fs";import path from "node:path";
import scopedLib from "@scoped/lib";import lib from "lib";
import myown from "@myown/package";
import aliased from "@/components/Button";import sibling from "./file.js";Each entry in the groups array is a group matcher that can be:
- A predefined group like
:NODE:,:BUN:, or:PACKAGE: - A glob pattern like
@my/lib/**; the action supports a limited set of globs. - Type-only imports like
{ "type": true } - A combination of the above, e.g.
[":BUN:", ":NODE:"] :BLANK_LINE:to insert a blank line between groups
Predefined groups
Section titled “Predefined groups”:URL:: sources starting withhttps://orhttp://:NODE:: Node.js built-in modules (node:path,fs,path, etc.):BUN:: Bun built-in modules (bun:test,bun, etc.):PACKAGE_WITH_PROTOCOL:: packages with a protocol (jsr:@my/lib,npm:lib):PACKAGE:: bare and scoped packages (lib,@scoped/lib):ALIAS:: path aliases starting with#,@/,~,$, or%:PATH:: absolute and relative paths
Type-only matcher
Section titled “Type-only matcher”Use a type-only matcher to separate import type from regular imports:
Setting "type": true matches only import type and export type statements.
Setting "type": false matches only non-type imports and exports.
Given the following configuration…
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ { "type": false, "source": [ "@my/lib", "@my/lib/**" ] }, [ "@my/lib", "@my/lib/**" ] ] } } } } }}…the following code…
import type { T } from "@my/lib";import { V } from "@my/lib";…is sorted as:
import { V } from "@my/lib";import type { T } from "@my/lib";identifierOrder
Section titled “identifierOrder”By default, attributes, imported and exported names are sorted with a natural sort order.
Opt for a lexicographic sort, also referred as binary sort,
by setting the identifierOrder option to lexicographic:
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "identifierOrder": "lexicographic" } } } } }}import { var1, var2, var21, var11, var12, var22 } from "my-package" with { "att10": "", "att2": "" };
export { var1, var2, var21, var11, var12, var22 };code-block.js:1:1 assist/source/organizeImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ The imports and exports are not sorted.
> 1 │ import { var1, var2, var21, var11, var12, var22 } from “my-package” with { “att10”: "", “att2”: "" };
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │
3 │ export { var1, var2, var21, var11, var12, var22 };
ℹ Safe fix: Organize Imports (Biome)
1 │ - import·{·var1,·var2,·var21,·var11,·var12,·var22·}·from·“my-package”·with·{·“att10”:·"",·“att2”:·""·};
1 │ + import·{·var1,·var11,·var12,·var2,·var21,·var22·}·from·“my-package”·with·{·“att10”:·"",·“att2”:·""·};
2 2 │
3 │ - export·{·var1,·var2,·var21,·var11,·var12,·var22·};
3 │ + export·{·var1,·var11,·var12,·var2,·var21,·var22·};
4 4 │
Note that this order doesn’t change how import and export sources are sorted.
Common configurations
Section titled “Common configurations”Group Node.js and bun built-in
Section titled “Group Node.js and bun built-in”The following example moves the Node.js and bun built-ins at the top of the file,
and adds a blank line just after them.
Other imports are placed after this blank line.
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ [ ":BUN:", ":NODE:" ], ":BLANK_LINE:" ] } } } } }}import { test } from "bun:test";import path from "node:path";import { A } from "@my/package";import { $ } from "bun";import fs from "fs";code-block.ts:1:1 assist/source/organizeImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ The imports and exports are not sorted.
> 1 │ import { test } from “bun:test”;
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │ import path from “node:path”;
3 │ import { A } from “@my/package”;
ℹ Safe fix: Organize Imports (Biome)
1 1 │ import { test } from “bun:test”;
2 2 │ import path from “node:path”;
3 │ - import·{·A·}·from·“@my/package”;
4 │ - import·{·$·}·from·“bun”;
5 │ - import·fs·from·“fs”;
3 │ + import·{·$·}·from·“bun”;
4 │ + import·fs·from·“fs”;
5 │ +
6 │ + import·{·A·}·from·“@my/package”;
6 7 │
Group monorepo packages
Section titled “Group monorepo packages”Let’s assume that all your monorepo packages are scoped by @mycompany.
The following example groups all monorepo imports after imports of external dependencies.
Because groups are matched in order, the first group has to exclude monorepo imports.
Indeed, :PACKAGE: matches imports like @mycompany/db, and thus must be excluded
thanks to the exception !@mycompany/**.
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ [ ":PACKAGE:", ":PACKAGE_WITH_PROTOCOL:", "!@mycompany/**" ], ":BLANK_LINE:", [ "@mycompany/**" ], ":BLANK_LINE:" ] } } } } }}import { Button } from "@mycompany/ui";import express from "express";import { db } from "@mycompany/db";import { handler } from "./handler.js";import { A } from "./file.js"code-block.js:1:1 assist/source/organizeImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ The imports and exports are not sorted.
> 1 │ import { Button } from “@mycompany/ui”;
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │ import express from “express”;
3 │ import { db } from “@mycompany/db”;
ℹ Safe fix: Organize Imports (Biome)
1 │ - import·{·Button·}·from·“@mycompany/ui”;
2 │ - import·express·from·“express”;
3 │ - import·{·db·}·from·“@mycompany/db”;
4 │ - import·{·handler·}·from·”./handler.js”;
5 │ - import·{·A·}·from·”./file.js”
1 │ + import·express·from·“express”;
2 │ +
3 │ + import·{·db·}·from·“@mycompany/db”;
4 │ + import·{·Button·}·from·“@mycompany/ui”;
5 │ +
6 │ + import·{·A·}·from·”./file.js”
7 │ + import·{·handler·}·from·”./handler.js”;
6 8 │
Group multiple libraries
Section titled “Group multiple libraries”In the following example, react and libraries like react-dom are grouped together.
A blank line separates them from the other imports placed directly below.
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ [ "react", "react/**", "react-*", "react-*/**" ], ":BLANK_LINE:" ] } } } } }}import lib from "lib";import { useState } from "react";import { render } from "react-dom/client";code-block.js:1:1 assist/source/organizeImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ The imports and exports are not sorted.
> 1 │ import lib from “lib”;
│ ^^^^^^^^^^^^^^^^^^^^^^
2 │ import { useState } from “react”;
3 │ import { render } from “react-dom/client”;
ℹ Safe fix: Organize Imports (Biome)
1 │ - import·lib·from·“lib”;
2 │ - import·{·useState·}·from·“react”;
3 │ - import·{·render·}·from·“react-dom/client”;
1 │ + import·{·useState·}·from·“react”;
2 │ + import·{·render·}·from·“react-dom/client”;
3 │ +
4 │ + import·lib·from·“lib”;
4 5 │
Place CSS/style imports last
Section titled “Place CSS/style imports last”The following example groups style imports together and place them after other imports.
Because groups are matched in order, the first group has to exclude style imports.
** matches everything and is followed by the two exceptions that exclude style imports.
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ [ "**", "!**/*.css", "!**/*.scss" ], ":BLANK_LINE:", [ "**/*.css", "**/*.scss" ] ] } } } } }}import "./styles/reset.css";import { useState } from "react";import styles from "./Component.module.css";import { Button } from "@/components/Button";code-block.js:1:1 assist/source/organizeImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ The imports and exports are not sorted.
> 1 │ import ”./styles/reset.css”;
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │ import { useState } from “react”;
3 │ import styles from ”./Component.module.css”;
ℹ Safe fix: Organize Imports (Biome)
1 1 │ import ”./styles/reset.css”;
2 2 │ import { useState } from “react”;
3 │ - import·styles·from·”./Component.module.css”;
4 │ - import·{·Button·}·from·”@/components/Button”;
3 │ + import·{·Button·}·from·”@/components/Button”;
4 │ +
5 │ + import·styles·from·”./Component.module.css”;
5 6 │
Group test utilities together
Section titled “Group test utilities together”The following example places test-related utilities at the top of the file. They are separated from other imports by a blank line.
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ [ "vitest", "vitest/**", "@testing-library", "@testing-library/**", "jest", "@jest/**" ], ":BLANK_LINE:" ] } } } } }}import { render } from "@testing-library/react";import { Button } from "@/components/Button";import { describe, it, expect } from "vitest";import { server } from "./mocks/server";code-block.js:1:1 assist/source/organizeImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ The imports and exports are not sorted.
> 1 │ import { render } from “@testing-library/react”;
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │ import { Button } from ”@/components/Button”;
3 │ import { describe, it, expect } from “vitest”;
ℹ Safe fix: Organize Imports (Biome)
1 1 │ import { render } from “@testing-library/react”;
2 │ - import·{·Button·}·from·”@/components/Button”;
3 │ - import·{·describe,·it,·expect·}·from·“vitest”;
2 │ + import·{·describe,·expect,·it·}·from·“vitest”;
3 │ +
4 │ + import·{·Button·}·from·”@/components/Button”;
4 5 │ import { server } from ”./mocks/server”;
5 6 │
Group import type and group export type
Section titled “Group import type and group export type”Use the following configuration to place import type and export type at the top of the file:
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ { "type": true } ] } } } } }}import { V } from "my-package";import type { T } from "my-package";code-block.ts:1:1 assist/source/organizeImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ The imports and exports are not sorted.
> 1 │ import { V } from “my-package”;
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │ import type { T } from “my-package”;
3 │
ℹ Safe fix: Organize Imports (Biome)
1 │ - import·{·V·}·from·“my-package”;
2 │ - import·type·{·T·}·from·“my-package”;
1 │ + import·type·{·T·}·from·“my-package”;
2 │ + import·{·V·}·from·“my-package”;
3 3 │
To place them after other imports, use the following configuration.
{ "assist": { "actions": { "source": { "organizeImports": { "options": { "groups": [ { "type": false } ] } } } } }}The following code…
import type { T } from "my-package";import { V } from "my-package";…is organized as:
import { V } from "my-package";import type { T } from "my-package";Note that you may want to use the lint rule
useImportType
and its style
to enforce the use of import type instead of import { type }.
With the following configuration…
{ "linter": { "rules": { "style": { "useImportType": { "level": "on", "options": { "style": "separatedType" } } } } }, "assist": { "enabled": true, "actions": { "source": { "organizeImports": "on" } } }}…the following code…
import type { T1 } from "a";import { type T2, V } from "a";…is organized as:
import type { T1, T2 } from "a";import { V } from "a";Maximize import merging with useImportType
Section titled “Maximize import merging with useImportType”To merge type-only imports (import type { T }) with regular imports (import { V }),
enable useImportType with inlineType:
{ "linter": { "rules": { "style": { "useImportType": { "level": "on", "options": { "style": "inlineType" } } } } }, "assist": { "enabled": true, "actions": { "source": { "organizeImports": "on" } } }}The following code…
import type { T } from "a";import { V } from "a";…is organized as:
import { type T, V } from "a";How it works
Section titled “How it works”This section provides an in-depth explanation of the internal mechanics of the action.
Import anatomy
Section titled “Import anatomy”First, let’s agree on the terminology that we will use in this section.
import A from "@my/lib" with { "attribute1": "value" };^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ kind source attributes
export * from "@my/lib" with { "attribute1": "value" };^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ kind source attributesChunks
Section titled “Chunks”Before sorting, imports and exports are divided into chunks. A chunk is a sequence of adjacent imports or exports. The action never moves imports or exports across chunk boundaries.
Chunks are separated by:
- Switching between imports and exports
- Any statement that is not an import or an export
- Bare imports also called side-effect imports (
import "polyfill"); Each forms its own chunk. - A comment followed by a blank line that we call a detached comment; See the comment handling section for more details.
The following example shows how imports and exports are chunked.
// chunk 1import A from "a";import * as B from "b";// chunk 2 (a bare import creates its own chunk)import "x";// Chunk 3import A from "a";
// Still same chunk (blank line alone doesn't split)import * as B from "b";// Detached comment (followed by blank line)
// New chunk starts hereimport { C } from "c";The action enforces the presence of a blank line between different chunks. Bare imports adjacent to a chunk of imports are not separated by a blank line.
Sorting within a chunk
Section titled “Sorting within a chunk”As described in the preliminary section, Imports and exports of a chunk are sorted by “distance” from the current file.
When two imports share the same source, they are ordered by kind:
- Namespace type import / Namespace type export
- Default type import
- Named type import / Named type export
- Namespace import / Namespace export
- Combined default and namespace import
- Default import
- Combined default and named import
- Named import / Named export
Imports and exports with attributes (with { ... }) are always placed first.
For example, the following code…
import * as namespaceImport from "same-source";import type * as namespaceTypeImport from "same-source";import type { namedTypeImport } from "same-source";import defaultNamespaceCombined, * as namespaceCombined from "same-source";import defaultNamedCombined, { namedCombined } from "same-source";import defaultImport from "same-source";import type defaultTypeImport from "same-source";import { importWithAttribute } from "same-source" with { "attribute": "value" } ;is sorted as follows:
import { importWithAttribute } from "same-source" with { "attribute": "value" } ;import type * as namespaceTypeImport from "same-source";import type defaultTypeImport from "same-source";import type { namedTypeImport } from "same-source";import * as namespaceImport from "same-source";import defaultNamespaceCombined, * as namespaceCombined from "same-source";import defaultImport from "same-source";import defaultNamedCombined, { namedCombined } from "same-source";This kind order cannot be changed.
Named specifier and attribute sorting
Section titled “Named specifier and attribute sorting”Named imports, named exports, and import attributes are also sorted, as shown in the following example.
import { a, b, A, B, c10, c9 } from "a";
export { a, b, A, B, c10, c9 } from "a";
import special from "special" with { "type": "ty", "metadata": "data" };code-block.js:1:1 assist/source/organizeImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ The imports and exports are not sorted.
> 1 │ import { a, b, A, B, c10, c9 } from “a”;
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │
3 │ export { a, b, A, B, c10, c9 } from “a”;
ℹ Safe fix: Organize Imports (Biome)
1 │ - import·{·a,·b,·A,·B,·c10,·c9·}·from·“a”;
1 │ + import·{·A,·a,·B,·b,·c9,·c10·}·from·“a”;
2 2 │
3 │ - export·{·a,·b,·A,·B,·c10,·c9·}·from·“a”;
3 │ + export·{·A,·a,·B,·b,·c9,·c10·}·from·“a”;
4 4 │
5 │ - import·special·from··“special”·with·{·“type”:·“ty”,·“metadata”:·“data”·};
5 │ + import·special·from··“special”·with·{·“metadata”:·“data”,·“type”:·“ty”·};
6 6 │
Import and export merging
Section titled “Import and export merging”Imports from the same source in the same chunk are merged when possible.
The following code…
import type { T1 } from "package";import type { T2 } from "package";import * as ns from "package";import D1 from "package";import D2 from "package";import { A } from "package";import { B } from "package";import { type T3 } from "package";…becomes:
import type { T1, T2 } from "package";import D1, * as ns from "package";import D2, { A, B, type T3 } from "package";With useImportType
set to separatedType, the result is:
import type { T1, T2, T3 } from "package";import D1, * as ns from "package";import D2, { A, B } from "package";Comment handling
Section titled “Comment handling”Comments directly above an import (attached comments) move with that import when it is sorted. Comments followed by a blank line (detached comments) stay in place and create a new chunk.
File-header comments, i.e. comments at the very top of the file, are always treated as detached, even without a blank line. This preserves copyright notices and license headers.
The following code…
// Copyright notice and file header commentimport F from "f";// Attached comment for `e`import E from "e";// Attached comment for `d`import D from "d";// Detached comment (new chunk)
// Attached comment for `b`import B from "b";// Attached comment for `a`import A from "a";…becomes:
// Copyright notice and file header comment
// Attached comment for `d`import D from "d";// Attached comment for `e`import E from "e";import F from "f";
// Detached comment (new chunk)
// Attached comment for `a`import A from "a";// Attached comment for `b`import B from "b";A blank line is automatically added after the header comment to ensure that the attached comment doesn’t merge with the header comment.
Supported glob patterns
Section titled “Supported glob patterns”A source is split into segments by /.
For example, src/file.js has two segments: src and file.js.
-
*: matches zero or more characters within a single segment;file.jsmatches*.js, butsrc/file.jsdoes not. -
**: matches zero or more segments and must be enclosed by/or be at the start/end;file.jsandsrc/file.jsboth match**/*.js. -
!: negates a pattern when used as the first character;file.jsmatches!*.test.js; Exceptions can be layered:["@my/lib/**", "!@my/lib/internal/**", "@my/lib/internal/allowed/**"]. -
\*: matches a literal*character. -
?,[,],{,}: reserved characters, must be escaped with\.
Related links
Section titled “Related links”Copyright (c) 2023-present Biome Developers and Contributors.