Migrating 1900+ Stories Storybook to Vite
“Storybook is a frontend workshop for building UI components and pages in isolation”
Storybook consists of two parts, manager and preview. The manager is the part where the sidebar, panels, add-ons, and previews are visible. Previews are just iframes, which are containing stories or docs. Previously, everything was built using webpack, you can override default webpack configs to make your workflow custom.
We at Fundamental Library are using a storybook for working on Fundamental-styles. It is a monorepo of multiple libraries, namely:
- fundamental-styles — Implementation of Fiori 3 and Horizon
- @fundamental-styles/fn — BTP design implementation
- @fundamental-styles/fn-icons — Horizon Icons
- @fundamental-styles/common-css — Common CSS classes and mixins
- @fundamental-styles/cx — SAP CX design implementation
So, as you might imagine we have a lot of stories. We have noticed that our build time and our dev server startup time have increased with every additional story file. Last year, when I had a chance to look at the progress of Vite in the storybook, I thought to myself that I would give it a year before considering moving to it. 10 months passed and I came back to the topic. Looks like, they did not spend any time on nonsense, because it looked like it was better than I anticipated. I immediately dived in and started to move away from my previously Webpack-based config to hopefully a faster vite-based config.
The steps for installation are straightforward, you can see them here
I am going to share my experience with it.
Many things, that require configuration on Webpack, are built-in in Vite. Things like TS support, SCSS, raw loading of files, or inline load. All of the things are coming with bundler. So as you might imagine, many of our custom plugins and loaders were removed immediately. That was pleasant.
The Dev server fires up in 10–20 seconds, as opposed to a 1-minute startup with Webpack. That’s nice, but when you cold-start your development server, the main work starts after dev-server. Vite starts to optimize dependencies, and vite-builder stores them in node_modules/.vite-storybook/deps
directory. Cold start is a slow process, although CLI reports a much better situation, practical time to interaction is not that great!
Package switcher
Since Vite is not bundling your modules into one bundle, with such a huge amount of stories, as you would expect, you have a lot of network requests. Initially, I had 900 requests sent in parallel, which was killing performance. In 10 seconds after starting the server I had the browser ready, but it would take additional 10–15 seconds to show me pages! That was painful. Solution? Use the experimental storyStoreV7
feature.
Previously when you would navigate to any story in the storybook UI, it would download all the stories, regardless of where you were located. It was ok when there were only several bundles, but it becomes problematic when you have hundreds of modules. When you request more than 500 files in parallel, in the browser, you’ll soon notice that everything is freezing and things that would’ve loaded instantly, are now stuck. So storyStoreV7
solves that by loading only the things related to the current location. It also brings new changes to how the story store works. For example, SET_STORIES
event is not fired at startup anymore and if you had addon or functionality depending on that behavior, you’ll have to update that functionality. That’s what happened to me.
We have multiple packages stored in our repository, so we wanted our stories in the sidebar to be grouped and have the ability to filter them out as well when the user wants to focus only on documentation coming from selected packages. To do that, you can use either event SET_STORIES
or use FullAPI’s method called setStories
, which will internally fire SET_STORIES
event and refresh the sidebar according to the new set of stories.
I would save the initial SET_STORIES
event output as original stories and then manipulate that initial list when the requested package had changed. But I have no list to work on when the initial event. So because of that small change, I had to alter my approach a little bit. Instead of relying on events, now I have to use useStorybookState
hook to tap into the inner state of the storybook. All of these are achieved with a custom addon.
Theme switcher
Before vite integration and the storybook’s builder swap, everything was working in webpack, so I had everything in the same bundler environment, which would give me the ability to share some logic between the manager and preview parts of the storybook, but now since some parts are going through Vite and some through Webpack(for now), I can not share same configurations without a slight change.
Our theming is based on CSS variables, plus because of multiple packages, there is a possibility that some of the packages will have a different set of themes or none at all. Vite has a cool out-of-the-box feature, it can read files either as a raw string, or pass them through plugins and get them as strings(like SASS output for example). webpack, which is used now for storybook manager build does not have that out of the box, it needs plugins to achieve it. I did not want to have a split logic for that and have several plugins for different environments. So I went ahead and separated the themes list into two different use cases, one is just a list, with no reference to the theming files, and one with a full description of the theme, including variables that are used in them. The structure looks like this:
As a result, we can have multiple sets of themes for multiple packages now and some of the configurations are shared between different environments.
Testing
Storybook has a great feature called storyshots, which is a fancy word for snapshot testing. It uses Jest and JSDom. Problem is that it is not compatible with Vite builder out of the box and making it work with Vite builder is a painful process with many pitfalls. For some people, it might be an absolute necessity to use storyshots, but for us, we only need to be sure that our HTML structure had not changed and since we use html
framework and not React or anything else, we can simply take story results strings and save them as snapshots. To make that testing as fast as possible, I went and used vitest
for the testing framework.
As for chromatic tests, those are the same for any builder, so no issues there.
Conclusion
Vite builder greatly improved the development experience for us, now HMR is instant, the load is very fast, and the start-up of the dev server is very fast, but I had to use some experimental features to avoid performance issues. For now, it works, but until storybook V7 is released, I can not be fully sure that this was the correct call and who knows how many breaking changes there will be related to the compatibility with Vite of storybook. I hope not that much, but we’ll see. In short term, I am very satisfied with the results but before migration, if you have some custom addons in your storybook, be extra cautious. Even if you do not have anything “custom”, there is no guarantee that 3rd party addons will continue to work as before. Heck! Even in the case of 1st parties, there is no guarantee for everything. Be careful!