describe(me) docs
early v0.12.1 browser mode · jsdom Live · Foundations
Examples npm GitHub

Getting started

Add one plugin to your Vitest config, run your tests and open the viewer. This page does it for browser mode and for jsdom.

Requirements #

Node
What your Vite and Vitest need. Vite 8 needs ^20.19.0 || >=22.12.0. The describe-me CLI needs ^20.16.0 || >=22.4.0.
Vitest
4 or 5 (^4.0.0 || ^5.0.0)
Vite
6.4 and later, 7 or 8 (^6.4.0 || ^7.0.0 || ^8.0.0)
React
18 or 19
Test library
vitest-browser-react 2 for browser mode, @testing-library/react 16 for jsdom

TypeScript, for props docs, and @testing-library/user-event are optional. Supported versions lists every range.

Install #

pnpm add -D describe-me @describe-me/vitest @describe-me/react

With npm or yarn:

npm install -D describe-me @describe-me/vitest @describe-me/react
yarn add -D describe-me @describe-me/vitest @describe-me/react vite

Yarn needs vite in the list because it is a peer of describe-me and yarn does not install peers. Keep all describe-me packages on one version, as One version for all packages explains. Package managers has the details for each.

Browser mode #

  1. Add the browser packages Skip the ones your project already has, then install Chromium.
    pnpm add -D vitest-browser-react @vitest/browser-playwright playwright
    pnpm exec playwright install chromium
  2. Add the plugin Put describeMe() into the plugins of your Vitest config.

    vitest.config.ts

    import { defineConfig } from 'vitest/config'
    import react from '@vitejs/plugin-react'
    import { playwright } from '@vitest/browser-playwright'
    import { describeMe } from '@describe-me/vitest/plugin' // describe-me
    
    export default defineConfig({
      plugins: [react(), describeMe()], // describe-me
      test: {
        browser: {
          enabled: true,
          provider: playwright(),
          instances: [{ browser: 'chromium' }],
        },
      },
    })
  3. Run the tests They write the frames to .describe-me/.
    pnpm vitest run
    What to commit says what to keep out of git.
  4. Open the viewer Start it, then open the URL it prints.
    pnpm describe-me dev
    By default that is http://localhost:6006. When the port is taken, the viewer takes the next free one.

Your existing tests need no change. The plugin redirects render from vitest-browser-react to a recording one, as What the plugin changes describes.

jsdom #

  1. Add the jsdom packages Skip the ones your project already has. @testing-library/dom is a peer of @testing-library/react.
    pnpm add -D @testing-library/react @testing-library/dom jsdom
    pnpm add -D @testing-library/user-event # optional
  2. Add the plugin Put describeMe() into the plugins of your Vitest config.

    vitest.config.ts

    import { defineConfig } from 'vitest/config'
    import react from '@vitejs/plugin-react'
    import { describeMe } from '@describe-me/vitest/plugin' // describe-me
    
    export default defineConfig({
      plugins: [react(), describeMe()], // describe-me
      test: { environment: 'jsdom' },
    })
  3. Run the tests They write the frames to .describe-me/.
    pnpm vitest run
  4. Open the viewer Start it, then open the URL it prints.
    pnpm describe-me dev

The plugin redirects render from @testing-library/react and turns on test.css unless you set it, so imported stylesheets reach the snapshots. See jsdom for the details and Which one to use to compare the two. What jsdom cannot do inside a test is in Limitations in jsdom.

Your first story #

Any test that renders a component is already a story. step() names a part of it:

src/Counter.test.tsx

import { describe, expect, it } from 'vitest'
import { render } from 'vitest-browser-react'
import { step } from '@describe-me/vitest'
import { Counter } from './Counter'

describe('Counter', () => {
  it('does not go below the minimum', async () => {
    const screen = await render(<Counter initial={1} />)
    await step('go down to the minimum', async () => {
      await screen.getByRole('button', { name: 'decrement' }).click()
    })

    await expect.element(screen.getByLabelText('value')).toHaveTextContent('0')
  })
})

In the viewer, Counter heads its file's block in the sidebar and the test is a story in it. Its timeline starts with the render frame. The click records a frame, and the step gives it its name, because its body produced that DOM. A closing frame follows only when the DOM changed after the last frame. Open the step frame in the browser example.

In jsdom the same test imports render from @testing-library/react, as the Disclosure test in the jsdom example does. See What records a frame and Naming steps with step().

Open the viewer #

Run describe-me dev next to Vitest in watch mode. Use two terminals, or one script like the examples:

package.json

"dev": "vitest & describe-me dev --data .describe-me; wait"

The viewer updates whenever the reporter rewrites the manifest. --data points it at another output directory.

The sidebar lists files, describe blocks and tests. The middle shows the stage and the frame timeline. The inspector shows the test status, the component, its props and the current frame. Finding your way walks through it, and The CLI lists the commands.

describe-me dev also starts the live preview from your Vitest config. Once it is ready, a test opens in Live, mounted from a fresh start. The Live toggle or L switches to its recorded frames. --no-live starts the viewer without it.

See your design system #

When the run found tokens, the viewer has a Foundations entry above the test files. It shows them: custom properties on :root and other root-like selectors in the CSS the tests load, and the Tailwind 4 theme. See the colors of the shadcn/ui example.

A JS theme, such as a Material UI or styled-components theme, needs the foundations.theme option. It names the module that exports the theme:

vitest.config.ts

import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import { describeMe } from '@describe-me/vitest/plugin'

export default defineConfig({
  plugins: [react(), describeMe({ foundations: { theme: 'src/theme.ts' } })],
})

Where the tokens come from lists every source, and foundations describes the option.

Publish the docs #

pnpm describe-me build

It writes a static site, the viewer with your data copied in, to describe-me-dist/. --out picks another directory. Serve it over HTTP, because it does not open from disk. describe-me build --live adds Live to the site, which then holds the compiled code of your components and tests.

Build the docs in CI shows the CI steps. Hosting the static site covers where to put it, and Fonts in the static site what the build downloads.

Next steps #