Skip to Content

Storybook

Storybook CSF and configuration rules from eslint-plugin-storybook.

They check Component Story Format conventions (default export meta, named story exports, play-function shape) and configuration pitfalls in .storybook/main.ts.

Source: eslint-plugin-storybook (MIT).

Rule index

Each rule name links to the detailed section below.

Examples come from the checked lint corpus  or package-level rule tests when project layout matters.

Rules

storybook/await-interactions

Require await on Storybook interaction helpers (userEvent, expect, waitFor, …) inside a play function.

The interactions addon intercepts the awaited promises to record steps, so a missing await skips that frame in the debugger and races the next assertion.

Example:

export default { component: Button }; export const Primary = { play: async () => { // reports: storybook/await-interactions (error) userEvent.click(button); }, };

storybook/context-in-play-function

Require forwarding the play-function context argument when invoking another story’s play function.

Storybook hangs the canvas, step tracker, and interactions addon off context; omitting it leaves the nested call without the runtime hooks it needs to drive the canvas.

Example:

export default { component: Button }; export const Primary = {}; export const Secondary = { play: async (context) => { // reports: storybook/context-in-play-function (error) Primary.play(); }, };

storybook/csf-component

Require the CSF default meta object to declare a component.

The reference unlocks Storybook’s auto-generated controls, prop-table docs, and CSF3 default render, without it those features silently no-op.

Example:

// reports: storybook/csf-component (error) export default { title: "Atoms/Button" }; export const Primary = {};

storybook/default-exports

Require every story file to provide the CSF default export.

Storybook keys all per-file configuration (title, decorators, parameters, component) off that default; files without it are skipped at indexing time.

Example:

// reports: storybook/default-exports (error) export const Primary = {};

storybook/hierarchy-separator

Reject the legacy | separator in Storybook story titles ("Foo|Bar").

Storybook 6 standardized on / for hierarchy and treats | as a literal character, so the title collapses into a single sidebar entry instead of nested folders.

The finding is tagged Deprecated, so an editor strikes the | separator through: the separator is superseded, not broken, and the story still renders.

Example:

export default { // reports: storybook/hierarchy-separator (warn) title: "Atoms|Button", component: Button, }; export const Primary = {};

storybook/meta-inline-properties

Require title and args in CSF meta to be inline literals, not references to outside variables or function calls.

Storybook’s indexer and upgrade codemods read these via static analysis and skip stories where the value is not literal.

Example:

const title = "Atoms/Button"; export default { // reports: storybook/meta-inline-properties (error) title, component: Button, }; export const Primary = {};

storybook/meta-satisfies-type

Require CSF meta objects to type-check with satisfies Meta<...> rather than a : Meta<...> annotation or as cast.

satisfies preserves the narrowed literal types so dependent StoryObj declarations can infer the component’s args precisely.

Example:

// reports: storybook/meta-satisfies-type (error) export default { component: Button }; export const Primary = {};

storybook/no-redundant-story-name

Reject name metadata on a story when it matches Storybook’s auto-derived name from the export identifier.

The explicit value adds boilerplate and drifts from the export when one side is renamed without the other.

The finding is tagged Unnecessary, so an editor greys the annotation out. Both shapes the rule reports are covered: the name / storyName property inside the story object and a standalone Story.storyName = ... assignment. The removable range includes a property’s trailing comma or an assignment’s semicolon, so deleting exactly what is faded leaves valid syntax.

Example:

export default { component: Button }; export const Primary = { // reports: storybook/no-redundant-story-name (warn) name: "Primary", };

storybook/no-renderer-packages

Reject direct imports from Storybook renderer packages (@storybook/react, etc.); use the user-facing package surface.

The diagnostic names the framework packages that replace the renderer, and each one is offered as an editor suggestion that rewrites the module specifier. @storybook/react offers @storybook/nextjs, @storybook/react-vite, and @storybook/react-webpack5. None is applied automatically: which one is right depends on the project’s bundler, which the import does not state, so ttsc fix and source.fixAll.ttsc leave the import alone.

Example:

// reports: storybook/no-renderer-packages (error) import type { Meta } from "@storybook/react"; export default { component: Button }; export const Primary = {};

storybook/no-stories-of

Reject the legacy storiesOf(...) builder API.

Storybook 7 removed it in favour of CSF default-export metadata; remaining uses block the migration to CSF3 and the modern indexer.

The finding is tagged Deprecated, so an editor strikes the import specifier through. The instruction is to migrate off the builder, not to delete the import and leave the calls below it unresolved.

Example:

import { // reports: storybook/no-stories-of (error) storiesOf, } from "@storybook/react"; storiesOf("Atoms/Button", module);

storybook/no-title-property-in-meta

Reject the title property in CSF meta when the project uses Storybook’s auto-title generation.

CSF3 derives the title from the file path, so an explicit title is redundant and drifts from the on-disk layout when files are moved.

The finding is tagged Unnecessary, so an editor greys the complete removable property out, including its trailing comma when present. Removing exactly the faded range is the resolution and leaves valid object syntax.

Example:

export default { // reports: storybook/no-title-property-in-meta (error) title: "Atoms/Button", component: Button, }; export const Primary = {};

storybook/no-uninstalled-addons

Validate Storybook addon names against the project’s dependencies, so misspelled addon ids surface at lint time.

Options:

  • packageJsonLocation?: string

    Explicit package.json path used to validate configured Storybook addons. When omitted, the rule walks upward from the linted config file.

  • ignore?: readonly string[]

    Addon package names to skip when checking installation status. Default: [ ].

Example:

export default { addons: [ "@storybook/addon-links", // reports: storybook/no-uninstalled-addons (error) "@storybook/addon-essentials", ], };

storybook/prefer-pascal-case

Require named story exports to use PascalCase.

Storybook derives the displayed story name from the export identifier and inserts spaces at case boundaries, so non-PascalCase exports render with awkward or merged labels in the sidebar.

Example:

export default { component: Button }; // reports: storybook/prefer-pascal-case (warn) export const primary = {};

storybook/story-exports

Require every story file to export at least one named story alongside the default meta.

A file with only the default export contributes nothing to the sidebar and usually means a story was deleted but the file was not.

Example:

// reports: storybook/story-exports (error) export default { component: Button }; export const __namedExportsOrder = ["Primary"];

storybook/use-storybook-expect

Require expect to be imported from @storybook/test in play functions, not from Jest.

The Storybook re-export is built for the browser interactions runner; Jest’s expect ships only Node-only matchers and throws when the play function executes in a browser preview.

Example:

export default { component: Button }; export const Primary = { play: () => { // reports: storybook/use-storybook-expect (error) expect(button).toBeVisible(); }, };

storybook/use-storybook-testing-library

Reject direct Testing Library imports inside story files; use the Storybook-bundled re-exports.

Example:

// reports: storybook/use-storybook-testing-library (error) import { screen } from "@testing-library/react"; export default { component: Button }; export const Primary = {};
Last updated on