DEV Community

Cover image for eslint-plugin-boundaries vs Steiger: Which One Should You Use for FSD?
Aleksandra Dudkina
Aleksandra Dudkina

Posted on Originally published at aleksandradudkina.hashnode.dev

eslint-plugin-boundaries vs Steiger: Which One Should You Use for FSD?

Recently, I wrote about using eslint-plugin-boundaries to enforce Feature-Sliced Design architecture in a React project.

The idea is simple: instead of relying on developers to remember which layers can import from which, let ESLint check it automatically.

But if you look into FSD tooling, I will get coming across another option: Steiger.

It's specifically designed for checking Feature-Sliced Design. So naturally, you might wonder:

Am I using the wrong tool? Should I replace eslint-plugin-boundaries with Steiger?

After comparing them, I don't think the answer is that simple.

What do they have in common?

Both tools can help prevent architectural violations.

For example, in FSD, a feature can depend on an entity, but an entity shouldn't depend on a feature.

// ✅ Allowed
// Features/Profile/ui/Profile.tsx

import { UserAvatar } from "@/Entities/User";
Enter fullscreen mode Exit fullscreen mode
// ❌ Forbidden
// Entities/User/ui/UserCard.tsx

import { EditProfile } from "@/Features/EditProfile";
Enter fullscreen mode Exit fullscreen mode

Both tools can detect this kind of incorrect dependency, but they approach the problem differently.

eslint-plugin-boundaries: Define your own architecture

eslint-plugin-boundaries is a general-purpose ESLint plugin. It doesn't know anything about FSD by default. Instead, you describe your architectural elements and the dependencies allowed between them.

For example:

{
  from: {
    element: { type: "features" },
  },
  allow: {
    to: {
      element: {
        type: ["entities", "shared"],
      },
    },
  },
}
Enter fullscreen mode Exit fullscreen mode

This tells ESLint that features may depend on entities and shared code. The advantage is flexibility. You can use the plugin with FSD, Clean Architecture, or your own architectural conventions.

You also don't have to follow specific folder naming conventions. In my project, for example, layers are capitalized (Features, Entities, Shared), and I can describe those paths explicitly.

The trade-off is that you have to define the rules yourself.

And if you forget an important architectural constraint, the plugin won't automatically enforce it.

Steiger: FSD rules out of the box

Steiger takes a different approach.

Instead of being a general-purpose architecture linter, it provides dedicated FSD checks through its official plugin.

It can validate things like:

  • Imports from higher layers or other slices on the same layer
  • Missing public APIs
  • Imports that bypass a slice's public API
  • Incorrect folder organization
  • Naming and segment conventions

For example, Steiger can detect when a slice doesn't expose a public API through an index.ts file. That's something my current boundaries/dependencies configuration doesn't check.

Getting started is straightforward:

npm install -D steiger @feature-sliced/steiger-plugin
Enter fullscreen mode Exit fullscreen mode
npx steiger ./src
Enter fullscreen mode Exit fullscreen mode

Steiger has its own CLI and configuration, separate from ESLint. It also supports watch mode.

The main advantage is that you don't need to manually describe every FSD convention. Its FSD rules also assume the conventional FSD project structure, including lowercase layer names. So adopting it in an existing project with different conventions may require additional changes.

Which one would I choose?

I don't think one is simply better than the other.

eslint-plugin-boundaries Steiger
Focus Custom dependency rules FSD-specific rules
Configuration Define your own architecture Built-in FSD conventions
Folder naming Flexible More opinionated
Integration ESLint Separate CLI
Checks beyond imports Additional configuration needed Built-in FSD structure checks

I'd choose eslint-plugin-boundaries when:

  • The project already has established architectural conventions.
  • The folder structure differs from standard FSD.
  • I mainly want to control dependencies between modules.
  • I want the checks integrated into the existing ESLint workflow.

I'd consider Steiger when:

  • Starting a new project with conventional FSD.
  • I want to validate more than import direction.
  • Public APIs, slices, segments, and folder organization are important to enforce automatically.
  • I'm comfortable using a separate architecture linter.

And there's no technical reason they can't be used together! ESLint can continue checking code quality and custom dependencies, while Steiger performs additional FSD-specific checks. Of course, I'd avoid enabling duplicate rules unless there's a good reason.

What did I decide for my project?

For now, I'm keeping eslint-plugin-boundaries.

My current configuration already solves the problem I introduced it for: controlling dependency direction between FSD layers. It's also integrated into ESLint, supports our existing folder structure, and doesn't require changing how the project is organized.

Steiger could catch additional issues, especially around public APIs and folder structure. That's useful, and something I may want to explore separately.

But I don't see a reason to replace a working solution just because another tool is more specialized.

Final takeaway

I think the main distinction is this:

eslint-plugin-boundaries enforces the architectural rules you define. Steiger checks your project against strict FSD conventions.

They overlap, but they aren't identical.

And when choosing between them, I'd start with the same question I use for most libraries:

What problem am I actually trying to solve?

If it's primarily dependency direction, eslint-plugin-boundaries might be enough. If it's broader FSD compliance, Steiger is worth considering.

The important thing isn't choosing the most specialized tool. It's making sure the tool actually enforces the rules your project needs.


References:

Top comments (1)

Collapse
 
susanacharya profile image
Susan Acharya •

the way you framed it, general purpose vs purpose built, makes the choice clear. a flexible plugin gives you control but you maintain the rules yourself, while steiger gives you the opinionated defaults for free. would be curious if running both in ci ever gets noisy