đź“Ł Storybook in Redwood is moving to Vite!

How does Storybook work today?

Today, when you run yarn redwood storybook, there are two important things to be aware of:

  • The config used lives in the @redwoodjs/testing package.
    • When Storybook was added to Redwood, Framework Packages did not yet exist. Therefore, we had to more tightly couple Storybook within the framework.
    • This is in contrast to the default place for Storybook config files, the in-project .storybook directory.
  • Storybook is built using Webpack.
    • This is simply because, as you may know, Webpack used to be the default bundler.

What is changing, and why?

Vite has been the default bundler for just over a year, and Storybook has been the primary holdout for this migration — until now!

Additionally, now that Storybook supports Framework Packages, we felt this migration was a natural time to move the Storybook config out of the monorepo and into your projects.

This means that you’ll get two primary advantages:

:rotating_light:Before you continue, please note:

  • Currently, only projects using Typescript are supported.
  • If you are still using Webpack, this is not for you, and you should consider switching to Vite :wink:

How do I upgrade?

Getting started is easy — you’ll first want to make sure you’re on at least v7.7.0, and then run:

yarn redwood storybook-vite

On first run, we’ll create the following config files for you:

  • web/.storybook/main.ts
  • web/.storybook/preview-body.html
    • This is required to change the id of the root div to redwood-app, which is what the entry file used by Vite requires.

Previously, we also allowed unplugin-auto-import, used by the Framework Package here, to create web/src/auto-imports.d.ts. If you’re on an early version, you may still have this, though this has been disabled.

  • Thanks @oliwheeler for reminding us to include this here!

Then, as usual, Storybook should open in your browser!

If you don’t have any of your own Storybook configuration, you should be good to go.

If you do have custom Storybook configuration, then you’ll need to manually migrate it over to the new files. For example, if you’ve got any global decorators, you can now just follow the official Storybook docs on that: Docs | Storybook

:bell: A note on migrating over web/config/storybook.preview.js (or generally working with the preview config file):

  • You’ll generally use web/.storybook/preview.{ts/js}.
  • However, if you’ve got any JSX in this file, you’ll instead need to use the {tsx/jsx} file extension.

Thank you @Philzen for calling this out!

And, of course, if you run into any problems or have any questions, we’d love to hear about it.

3 Likes

I’m only getting a bunch of these:

import_react_dom.default.unmountComponentAtNode is not a function

I thought it was due to my locale setup, but even after I did that, it doesn’t seem to work.

Even if I just do yarn rw g component TestComponent that component throws with that error in storybook.

Interestingly to get GitHub - stevensacks/storybook-react-i18next: Storybook i18next addon to work, I needed to manually install @storybook/icons, is that intended?

That is super weird - thanks for letting us know, I’ll generate a component and check it out! Would you mind also providing anything more about your setup that may be helpful?

As for add ons, if you take a look at your new web/.storybook/main.ts file, you’ll see that the only add on we’re installing for you is @storybook/addon-essentials. By comparison, you can see what addons are included in the Webpack one here — the only difference is that the Webpack one optionally includes @storybook/addon-a11y.

I don’t know anything about storybook-react-i18next specifically, though generally the requirements of an add on are the purview of that add on. However, we can definitely explore having the new Storybook setup ship with more installed by default. Were you previously using that add on with the Webpack version?

EDIT: @razzeee, I’m having trouble reproducing your error:

Can you tell me a little more? Have you tried doing git clean and/or yarn cache clean?

Just tried that, didn’t help

No, we were using a custom config/storybook.preview.js:

import i18n from 'i18next'
import detector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'

import de from '../src/i18n/de/common.json'
import en from '../src/i18n/en/common.json'

i18n
  .use(detector)
  .use(initReactI18next)
  .init({
    resources: {
      en: en,
      de: de,
    },
    fallbackLng: 'en',

    interpolation: {
      escapeValue: false,
    },
  })

But it seemed easier to use that addon.

Just was wondering, if a default storybook (non redwood) ships the icons package by default, as the readme of GitHub - stevensacks/storybook-react-i18next: Storybook i18next addon doesn’t mention it at all.

Uncaught (in promise) TypeError: import_react_dom.default.render is not a function
    renderElement react-16.mjs:3
    renderElement react-16.mjs:3
    render chunk-VT6XBPNA.js:2394
    promise callback*DocsRenderer/this.render/< chunk-VT6XBPNA.js:2394
    render chunk-VT6XBPNA.js:2393
    renderDocs runtime.js:92
    renderToElement runtime.js:92
    renderSelection runtime.js:101
    selectSpecifiedStory runtime.js:101
    initializeWithStoryIndex runtime.js:92
    _runResolutions runtime.js:4
    _runResolutions runtime.js:4
    then runtime.js:4
    initializeWithStoryIndex runtime.js:92
    initializeWithProjectAnnotations runtime.js:87
    promise callback*initializeWithProjectAnnotations runtime.js:87
    initialize runtime.js:81
    _runResolutions runtime.js:4
    _runResolutions runtime.js:4
    _setResolved runtime.js:4
    _continueWith runtime.js:4
    _runResolutions runtime.js:4
    _runResolutions runtime.js:4
    _setResolved runtime.js:4
    _continueWith runtime.js:4
    _handleUserFunctionResult runtime.js:4
    _runResolutions runtime.js:4
    _runResolutions runtime.js:4
    _setResolved runtime.js:4
    _continueWith runtime.js:4
    _chainPromiseData runtime.js:4
    promise callback*_chainPromiseData runtime.js:4
    _handleUserFunctionResult runtime.js:4
    _runResolutions runtime.js:4
    _runResolutions runtime.js:4
    then runtime.js:4
    getProjectAnnotationsOrRenderError runtime.js:81
    initialize runtime.js:81
    <anonymous> vite-app.js:25
react-16.mjs:3:65

I thought it might be caused by headlessui-float, but that didn’t turn out to be true.

It also seems to happen, if I just take our project and migrate to storybook vite without changing anything of the default addon. I can try one of our other redwood projects.

I’m out of ideas for now, I’ve taken our smallest project and migrated it, added a new TestComponent and removed everything else from the storybook (or well that solution) - even trimmed down the App.tsx to the minimum. The error is still there.

Only thing I can still think of, is comparing our tsconfig.json with the default one

Thanks for this info!

My understanding is that by default, Storybook ships only with these essential add ons: Essential addons • Storybook docs

How did you figure out that you needed to include the icons add on?

On your error call stack, the one thing that jumps out to me is — are you using React 16? I think Redwood has been using React 18 since v5…

Otherwise, if you don’t have a repo that you can share, can you please provide repro steps?

Building storybook complained, that it needs that package, so that was very obvious.

Hrm, that’s weird. The project has been around for some time, so that might fit with when it was created, but we’re not referencing that.

❯ yarn why react
├─ @redwoodjs/auth@npm:8.0.0-canary.757
│  └─ react@npm:19.0.0-beta-04b058868c-20240508 (via npm:19.0.0-beta-04b058868c-20240508)
│
├─ @redwoodjs/vite@npm:8.0.0-canary.757
│  └─ react@npm:19.0.0-beta-04b058868c-20240508 (via npm:19.0.0-beta-04b058868c-20240508)
│
└─ web@workspace:web
   └─ react@npm:19.0.0-beta-04b058868c-20240508 (via npm:19.0.0-beta-04b058868c-20240508)

Tried to delete and regenerate yarn.lock but doesn’t help with the error

1 Like

I might be able to find some time in the next days, to clean out one of our projects and hopefully share it.

1 Like

Sounds good! :slight_smile: This would definitely be helpful as I can’t reproduce it otherwise.

Figured out how to repro! Opened a bug here: [Bug?]: Upgrading Redwood to Canary causes "unmountComponentAtNode is not a function" in Storybook · Issue #10815 · redwoodjs/redwood · GitHub

1 Like

fyi @razzeee this seems to be an issue specifically with upgrading an existing project to Canary — can you instead upgrade to v7.7.0 and see if it works?

I tied that today and abandoned it, as it installed the canary of the vite dep and that felt weird, can try again tomorrow

I can get it to work, using a new branch, will investigate, what’s wrong with the other branch.

But I’m also running into useAuth must be used within an AuthProvider on some stories now, that worked with the old bundler.

Somehow react 19 snuck into the old branch and that seems to break it

image

Edit:
@arimendelow

Now looking at it with time, it seems quiet easy to solve, just add a new decorator:

  decorators:[
      (Story) =>
        <AuthProvider>
          <Story />
        </AuthProvider>
    ]

You might have to make your file into a tsx for it to work. Should probably be part of the default setup. Then again, it might just be our auth setup being custom-auth.

Hmmm this is interesting — here’s the providers provided in the framework package: redwood/packages/storybook/src/mocks/MockProviders.tsx at 986edb48fd4b1865f30c7fbae989888577c81e23 · redwoodjs/redwood · GitHub

In my early testing, this worked. I wonder if something changed. For comparison, this is the same we do for the old bundler: redwood/packages/testing/src/web/MockProviders.tsx at main · redwoodjs/redwood · GitHub

Where are you getting the <AuthProvider /> that you’re using in the decorator?

I can post something more detailed tomorrow, but in a nutshell, we’re calling redwood/packages/auth/src/authFactory.ts at fdc98dccfc91e5e4b1d8ea1344ce8bfcc06b90ec · redwoodjs/redwood · GitHub to create these with our custom auth implementation

1 Like

We get this from our custom auth implementation, the top level looks like this

import { GitlabAuth } from 'api/src/lib/gitlabAuth'

import { createAuthentication } from '@redwoodjs/auth'

const clientId = process.env.GITLAB_CLIENT_ID
const redirectUri = process.env.GITLAB_REDIRECT_URI
const authority = process.env.GITLAB_AUTHORITY

const client = new GitlabAuth(clientId, redirectUri, authority)

function createAuth() {
  const authImplementation = createAuthImplementation(client)

  return createAuthentication(authImplementation)
}

function createAuthImplementation(client: GitlabAuth) {
  return {
    type: 'custom-auth',
    client,
    login: async () => {
      await client.makeAuthorizationRequest()
    },
    restoreAuthState: async () => {
      if (
        window?.location?.search?.includes('code=') &&
        window?.location?.search?.includes('state=')
      ) {
        await client.completeAuthorizationRequestIfPossible()
      }
    },
    logout: async () => await client.signOut(),
    getToken: async () => await client.getToken(),
    getUserMetadata: async () => await client.getUserMetadata(),
    signup: async () => {
      throw new Error('Not implemented')
    },
  }
}

export const { AuthProvider, useAuth } = createAuth()

1 Like

Thanks heaps for this effort! DX on Vite is simply more fun – not only in terms of build speed, but also i find that sometimes changes to some files (i.e. included SVGs) were missed on webpack hot reload. Great that the Storybook setup now catches up.

My two cents i’d like to add to the migration post:

Migrating custom storybook configuration

Transition seems to be straightforward:

  1. Migrate contents of web/config/storybook.config.js to web/.storybook/main.ts
    For more complex configurations, this may now show some red squiggles – in my case there were deprecated options that i missed during the RW 6 upgrade, storybook/MIGRATION.md at v7.6.19 · storybookjs/storybook · GitHub helped a lot as it seems to be the most comprehensive reference for that step.
  2. Move web/config/storybook.preview.js to web/.storybook/preview.js (or preview.ts respectively)
    2a. If your preview file contains any JSX – i.e. when configuring a default decorator (which you most likely have in Chakra UI setups, for instance) – the file type must be changed to jsx (or tsx respectively)

Mind the Doc versions

That link will bring up the Storybook 8 docs (while RW 7.7.0 is using Storybook 7) – so this should probably be Docs | Storybook to be on the safe side.

Same applies to

… which should be Docs | Storybook instead.

Thanks for the feedback! :slight_smile: Glad you’re enjoying — I totally agree about the improved DX.

These are all good callouts — and excellent note on needing to use the proper extension if you have any JSX in your preview config. I’ve been caught by that too :slight_smile:

I’ll update the post (and docs).

Keep us posted if you run into any issues or have any additional feedback!

1 Like