DEV Community

Cover image for I missed Jetpack Compose's @Preview in React, so I built a Vite plugin for it
yuntaeko
yuntaeko

Posted on

I missed Jetpack Compose's @Preview in React, so I built a Vite plugin for it

I'm a React developer, and I've written my share of Storybook stories too.

Still, a lot of my day looked like this: build a component, open the app in the browser, click through to the screen where it shows up, and check whether it looks right. Then change something and do it all again.

Storybook works best when you build components in it first and then build features on top of them. In practice, feature deadlines often flipped that order for me. The feature shipped first, and the stories got written afterwards in a hurry, if at all.

Then I saw Compose

At some point I got to look at an Android project written with Jetpack Compose, and its @Preview felt like a different world:

@Preview
@Composable
fun PrimaryButtonPreview() {
    PrimaryButton(text = "Save")
}
Enter fullscreen mode Exit fullscreen mode

The syntax was tiny. You write only the cases you care about, right next to the code, and you see them while you work. No separate setup to
keep alive.

As a React developer, I wanted exactly that. So I picked the setup I use most, React with Vite. I'd also wanted to build a Vite plugin for a
while, and the two ideas met.

That became Vitrine, and 0.2.0 just came out.

The Vitrine gallery with a Button preview, a sidebar of grouped components and a props panel

One comment, your own Vite

You add a comment above a component you export:

/** @preview */
export const PrimaryButton = () => <Button tone="primary">Save</Button>;
Enter fullscreen mode Exit fullscreen mode

and open /__vitrine on the Vite dev server you're already running. The component is there.

Setup is one plugin next to your React plugin:

// vite.config.ts
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import vitrine from "vite-plugin-react-vitrine";

export default defineConfig({
  plugins: [react(), vitrine()],
});
Enter fullscreen mode Exit fullscreen mode

Because the previews go through your own dev server, aliases, CSS, plugins and HMR behave exactly like in your app.

You can try it without installing anything in the live demo.

Writing only the cases you care about

Like Compose previews, you can add a few named cases when one isn't enough:

import { preview } from "vite-plugin-react-vitrine/preview";

preview(Button, {
  args: { children: "Save" },
  variants: {
    primary: { name: "Primary", args: { tone: "primary" } },
    danger: { name: "Danger", args: { tone: "danger" } },
  },
});
Enter fullscreen mode Exit fullscreen mode

Matrix view rendering a button in every combination of tone and size

The gallery also gives you:

  • Props controls generated from your TypeScript prop types
  • Variants side by side, and a Matrix that renders every combination of two props
  • Actions, a log of calls to onClick, onChange and other on* props
  • Globals for values like theme or locale that a provider sets
  • Viewports that trigger real media queries, since each preview renders in its own frame
  • History to compare the current version with an earlier save
  • Links that reopen the same preview, variant and values

And when you want to share the gallery with reviewers, vitrine-plugin-static builds it into static files. The live demo is built that way.

Next to your editor, if you want

The closest thing to the Compose experience is the optional VS Code extension. It opens the same gallery in a panel beside your code, and as you move your cursor between @preview exports, the panel switches to that preview. It can also save the props you set in the gallery as a new variant in your file.

You don't need it, though. The gallery is just a page on your dev server, so it works in any browser.

Try it

npm install -D vite-plugin-react-vitrine
Enter fullscreen mode Exit fullscreen mode

It supports React 18 and 19 and Vite 6.4 and later.

What's next

Vitrine is still 0.x, and I'd love to hear how it fits into your workflow: what breaks in your setup, and what would make it something you reach for every day. If you'd like to contribute, I've opened a few beginner-friendly issues on GitHub.

I'm from Korea and English isn't my first language, so sorry if anything reads a bit off. Thanks for reading!

Top comments (0)