npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eslint-plugin-layered-imports

v0.1.0

Published

Organize imports into readable groups for FSD and layered frontend projects.

Readme

Why?

Layered frontend architectures help teams manage dependencies, but import sections can quickly become noisy and inconsistent.

This plugin aims to make imports easier to scan by grouping them by source type and enforcing readable spacing between groups.

Example

import path from "node:path";

import React from "react";
import { clsx } from "clsx";

import { userApi } from "@/entities/user";
import { Button } from "@/shared/ui";

import styles from "./style.module.css";
import { helper } from "../lib/helper";

Installation

pnpm add -D eslint-plugin-layered-imports
npm install -D eslint-plugin-layered-imports

Usage

Quick start with the FSD preset

Use the FSD-friendly preset to enable the plugin with sensible defaults for layered frontend projects.

The preset treats @/ imports as internal imports, orders top-level groups as builtin, external, internal, relative, and orders internal imports by the common FSD layer order: app, pages, widgets, features, entities, shared.

import layeredImports from "eslint-plugin-layered-imports";

export default [layeredImports.configs.fsd];

The preset is equivalent to enabling the rule with these defaults:

{
  internalAliases: ["@/"],
  groups: ["builtin", "external", "internal", "relative"],
  internalLayerOrder: [
    "app",
    "pages",
    "widgets",
    "features",
    "entities",
    "shared",
  ],
}

You can override preset defaults by adding another config object after it.

import layeredImports from "eslint-plugin-layered-imports";

export default [
  layeredImports.configs.fsd,
  {
    rules: {
      "layered-imports/import-spacing": [
        "error",
        {
          internalAliases: ["@/", "~/"],
        },
      ],
    },
  },
];

Manual flat config setup

Use manual setup when you do not want the FSD preset or want to configure every option yourself.

import layeredImports from "eslint-plugin-layered-imports";

export default [
  {
    plugins: {
      "layered-imports": layeredImports,
    },
    rules: {
      "layered-imports/import-spacing": "error",
    },
  },
];

With options:

import layeredImports from "eslint-plugin-layered-imports";

export default [
  {
    plugins: {
      "layered-imports": layeredImports,
    },
    rules: {
      "layered-imports/import-spacing": ["error", {
        internalAliases: ["@/", "~/"],
        groups: ["builtin", "external", "internal", "relative"],
        internalLayerOrder: ["features", "entities", "shared"],
      }],
    },
  },
];

Rules

import-spacing

Requires a blank line between different import groups.

The initial groups are:

  • builtin: Node.js built-in modules such as node:path or fs
  • external: npm package imports such as react or clsx
  • internal: project-local alias imports such as @/shared/ui
  • relative: relative imports such as ./helper or ../lib/helper

Valid and invalid examples

Invalid:

import helper from "./helper";
import React from "react";

Valid:

import React from "react";

import helper from "./helper";

With internalLayerOrder, internal imports can also be ordered by layer.

Invalid:

import { Button } from "@/shared/ui/button";
import { UserCard } from "@/entities/user";
import { LoginForm } from "@/features/auth";

Valid:

import { LoginForm } from "@/features/auth";
import { UserCard } from "@/entities/user";
import { Button } from "@/shared/ui/button";

Options

| Option | Type | Default | Description | | --- | --- | --- | --- | | internalAliases | string[] | ["@/"] | Source prefixes treated as internal imports. | | groups | Array<"builtin" \| "external" \| "internal" \| "relative"> | ["builtin", "external", "internal", "relative"] | Top-level import group order. | | internalLayerOrder | string[] | undefined | Optional order inside the internal group. |

internalAliases

Type: string[]

Default: ["@/"]

Configures which import source prefixes should be treated as the internal group.

export default [
  {
    plugins: {
      "layered-imports": layeredImports,
    },
    rules: {
      "layered-imports/import-spacing": [
        "error",
        {
          internalAliases: ["@/", "~/", "@app/"],
        },
      ],
    },
  },
];

Configured aliases are matched as prefixes. Use specific prefixes such as @/ or @app/ instead of a broad @ prefix so scoped packages such as @tanstack/react-query remain external.

groups

Type: Array<"builtin" | "external" | "internal" | "relative">

Default: ["builtin", "external", "internal", "relative"]

Configures the expected order of import groups. The array must include each group exactly once.

export default [
  {
    plugins: {
      "layered-imports": layeredImports,
    },
    rules: {
      "layered-imports/import-spacing": [
        "error",
        {
          groups: ["builtin", "external", "internal", "relative"],
        },
      ],
    },
  },
];

Imports are reported when a later group appears before an earlier configured group.

internalLayerOrder

Type: string[]

Default: undefined

Configures an optional order inside the internal group. The rule removes the matching internalAliases prefix, reads the first path segment as the internal layer, and orders known layers by this array.

export default [
  {
    plugins: {
      "layered-imports": layeredImports,
    },
    rules: {
      "layered-imports/import-spacing": [
        "error",
        {
          internalAliases: ["@/"],
          internalLayerOrder: [
            "app",
            "pages",
            "widgets",
            "features",
            "entities",
            "shared",
          ],
        },
      ],
    },
  },
];

For example, @/shared/ui/button is treated as the shared layer. Known layers are ordered before unknown internal paths. Unknown internal paths keep their existing relative order. Imports in the same known layer are sorted by source path.

Autofix behavior

The rule can autofix safe import group order violations. When imports are in the same contiguous import block, --fix reorders them by the configured groups order and normalizes blank lines between groups.

npx eslint . --fix
import helper from "./helper";
import React from "react";

is fixed to:

import React from "react";

import helper from "./helper";

Autofix is intentionally conservative:

  • It does not move imports across non-import statements.
  • It does not reorder an import block that contains side-effect imports such as import "./setup";.
  • Leading comments attached to an import move together with that import.
  • Imports inside the same group keep their existing relative order unless internalLayerOrder is configured for internal imports.

Limitations

  • The rule only manages static import declarations.
  • It does not sort named specifiers inside a single import declaration.
  • internalLayerOrder reads the first path segment after a matching internal alias. For example, @/shared/ui/button is treated as the shared layer.
  • Unknown internal layers are placed after known layers and keep their existing relative order.

Roadmap

  • [x] Add import-spacing rule
  • [x] Support configurable import group order
  • [x] Add auto-fix support
  • [x] Add FSD-friendly preset
  • [x] Support optional internal layer ordering