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

storybook-react-variants

v0.1.1

Published

Auto-generates a Storybook story rendering all combinations of component prop variants

Readme

storybook-react-variants

Generates a Storybook story that renders every combination of your component's prop variants in a matrix grid — columns × rows with axis labels — and adds a live "Available variants" checkbox control to toggle dimensions at runtime.

Installation

npm install storybook-react-variants
# or
yarn add storybook-react-variants

Quick start

// Button.stories.tsx
import { createVariantsStory } from 'storybook-react-variants'
import { Button } from './Button'

export default { component: Button }

export const Variants = createVariantsStory(Button)

That's it. createVariantsStory reads __docgenInfo (injected by react-docgen-typescript-loader or Storybook's default Webpack config) and auto-detects all variant props:

  • String literal unions'sm' | 'md' | 'lg' → columns or rows
  • Booleanstrue / false

Props that are plain string, number, functions, etc. are ignored.

The returned object has render, argTypes, and args so Storybook picks it up as a standard CSF3 story with working controls.


Matrix layout

With a Button that has type: 'primary' | 'secondary' | 'ghost' and size: 'sm' | 'md' | 'lg':

            sm        md        lg
primary  │ <Button> <Button> <Button>
secondary│ <Button> <Button> <Button>
ghost    │ <Button> <Button> <Button>
  • First prop → columns
  • Second prop → rows
  • 3+ props → stacked labeled blocks, one per combination of the remaining props

Full example

// Button.stories.tsx
import type { Meta } from '@storybook/react'
import { createVariantsStory } from 'storybook-react-variants'
import { Button } from './Button'

// Button props:
//   type: 'primary' | 'secondary' | 'ghost'
//   size: 'sm' | 'md' | 'lg'
//   disabled: boolean
//   label: string

const meta: Meta<typeof Button> = { component: Button }
export default meta

export const Variants = createVariantsStory(Button, {
  baseArgs: { label: 'Click me' },
})

This renders a 3 × 3 grid for type × size, stacked twice for disabled: true and disabled: false. In the Controls panel a "Available variants" checkbox group lets you toggle any dimension on or off without touching code.


Options

baseArgs

Props applied to every rendered variant. Use for required non-variant props.

createVariantsStory(Button, {
  baseArgs: { label: 'Click me' },
})

onlyArgs

Limit which props are used as variant dimensions. Everything else is ignored even if docgen detects it.

createVariantsStory(Button, {
  onlyArgs: ['type', 'size'],
})

overrideArgTypes

Replace or extend the auto-detected values for a prop.

createVariantsStory(Button, {
  // only show two of the three sizes
  overrideArgTypes: { size: ['sm', 'lg'] },
})

defaultDisabledVariants

Props that start unchecked in the "Available variants" control. Everything else starts checked. Useful when a prop produces a large grid that you rarely need.

createVariantsStory(Button, {
  defaultDisabledVariants: ['disabled'],
})

gridComponent

Replace the built-in matrix renderer with your own. Receives VariantsGridProps:

import type { VariantsGridProps } from 'storybook-react-variants'

function MyGrid({ blocks }: VariantsGridProps) {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 32 }}>
      {blocks.map((block, i) => (
        <div key={i}>
          {block.label && <h4>{block.label}</h4>}
          <div style={{ display: 'grid', gridTemplateColumns: `repeat(${block.colAxis?.values.length ?? 1}, auto)`, gap: 8 }}>
            {block.cells.flat().map((cell, j) => (
              <div key={j}>{cell}</div>
            ))}
          </div>
        </div>
      ))}
    </div>
  )
}

export const Variants = createVariantsStory(Button, {
  gridComponent: MyGrid,
})

variantWrapper

Wrap every rendered component instance — useful for providing context (theme, router, i18n) or adding a decorative border.

import type { PropsWithChildren } from 'react'
import type { ButtonProps } from './Button'

function DarkBackground({ children }: PropsWithChildren<{ variantProps: ButtonProps }>) {
  return (
    <div style={{ background: '#1a1a1a', padding: 16, borderRadius: 8 }}>
      {children}
    </div>
  )
}

export const VariantsDark = createVariantsStory(Button, {
  variantWrapper: DarkBackground,
})

The wrapper receives the full variantProps object if you need to inspect what's being rendered:

function LabeledCell({ variantProps, children }: PropsWithChildren<{ variantProps: ButtonProps }>) {
  return (
    <div>
      <div style={{ fontSize: 11, color: '#999', marginBottom: 4 }}>
        {Object.entries(variantProps).map(([k, v]) => `${k}=${v}`).join(' · ')}
      </div>
      {children}
    </div>
  )
}

Exported types

import type {
  VariantsGridProps,     // props for gridComponent
  RenderedMatrixBlock,   // one block in the matrix (colAxis, rowAxis, cells[][])
  MatrixAxis,            // { propName: string; values: unknown[] }
  MatrixBlock,           // internal data block before rendering
} from 'storybook-react-variants'

VariantCell is also exported — a minimal styled wrapper you can use inside a custom gridComponent to match the built-in cell appearance:

import { VariantCell } from 'storybook-react-variants'

How variant detection works

Detection reads Component.__docgenInfo.props which is injected at build time. Both react-docgen formats are supported:

| Format | Detected as variant | |--------|-------------------| | TypeScript: type: 'a' \| 'b' | ✅ string literal union | | TypeScript: disabled: boolean | ✅ → [true, false] | | TypeScript: label: string | ❌ ignored | | PropTypes: PropTypes.oneOf(['a', 'b']) | ✅ | | PropTypes: PropTypes.bool | ❌ not detected (no docgen type info) |

If your component doesn't have __docgenInfo, pass values explicitly via overrideArgTypes.