TL;DR — “Storybooking” is the process of organizing project and component requirements (a.k.a. “stories”) in such a way that all aspects of the user-interfacing parts can be communicated to all stakeholders. This article introduces the reader to the [tiny!*] nuxt-stories module which aims to make storybooking relatively painless and interesting for the Nuxt framework.
Disclaimer: I am the author of the nuxt-stories module. (*I’ll try my best to keep it tiny…)
Documenting and communicating requirements properly is arguably the most important part of any project. Yet, in many projects, it seems that requirements very easily become disconnected from actual code written. Usually requirements are written in separate static locations, such as spreadsheets or slide decks, and the code is written somewhere else. This sounds like a fairly simple way to organize concerns, but becomes problematic when mismatches occur between the one-time written expectations and a constantly-changing reality. There is often a need to keep iterating on requirements and code until expectations are met. This is especially true for web front-ends, where many requirements can be completely subjective (i.e., based on aesthetic).
If the simple English requirements can’t be ironed out correctly, the code doesn’t stand a chance!
Storybooking tries to address the disconnect by placing requirements, a.k.a. “stories”, directly in the same workspace as the codebase. This way, if we can think of each page of a web application simply as a collection of components and layouts, the stories have easy access to those. Ideally, as the storybooker writes the stories, the actual components are being written and tested. So when it is time for those components to be used in actual pages, the pages will always be in sync with the requirements.
In the above picture, it’s ideal to have the same components shared and synced with stories, tests and pages. From my experience, when stories live in scattered locations, and, sometimes, written with conflicting or unclear requirements, it only serves to delay real progress. Understandably, someone in control of the project requirements may have the best intentions and well-thought out ideas, but may soon realize major tweaks are needed. Storybooking will show what tweaks are needed immediately.
While it is true that components can be designed directly on the pages, the developer will soon find out that designing them in isolation from pages will be far easier. Sometimes it’s desired to see what components would look like with dummy data rather than real data because…the app hasn’t been created yet! But, even if the app is in production and storybooking came after the fact (gasp!), it is usually not desired to pollute the pages folders with “dummyData.json” files. It’s better to let that live in perhaps the stories folders (or dummy DBs!), and in special “ux/flavor-123” git branches. Then, quickly changing the view is simply a matter of changing the git branch.
Storybooking in Nuxt:
There are many great storybooking solutions out there, many with great communities, features, and add-ons. I have a huge amount of respect for those solutions! Those are far more seasoned than the Nuxt module I developed a week ago. However, I chose to create the module because when it came to Nuxt, I faced several challenges when I wanted to storybook and still use some of the “Nuxt stuff” and do things the “Nuxt way”.
Eventually, after spending several hours on this problem, I had the ~grande realization~ that Nuxt already provides a strong architecture that can simply be extended to include storybooks when desired (i.e., in development mode), and omitted when not (in production). This means that with the module, enabling storybooking is as simple as just registering the module in Nuxt config. Long gone is the need to set aside a whole Sunday just to get storybooking working.
- The nuxt-stories module depends on: glob, pify, and @nuxt/utils. These should have already been installed after running
npx create-nuxt-app. If they weren’t, install them:
npm i -D glob pify @nuxt/utils
2. Install the module:
npm i -D nuxt-stories
The module will also install bootstrap, which is used to style the sample stories. After installing, a post-install script will automatically run to copy in sample stories to a
.stories directory in your project folder to help you get started asap. Rest assured, it will “gently copy”, so if you already have contents in that folder, they won’t get overwritten.
nuxt.config.js file, just specify
nuxt-stories as one of your
buildModules. It will only be enabled if your environment is in development mode OR if you specify
forceBuild: true. There might be times where you want to host the app as a static site so that you can also share the stories. However, normally in production, you probably wouldn't want to package them.
As mentioned before, when you first installed the module, the
.stories folder should have been created with example stories in it. This folder is treated exactly like your pages folder, except that your stories go in this one. Routes are auto-created using the same
createRoutes() utility that Nuxt uses to create your pages routes, so you can structure your stories similarly. To avoid conflicting with any real routes that might be named "stories", the story routes will start at ".stories". In addition, you might find it useful in your workflow to have the ".stories" really close to your "components" folder in your working tree. It should be much faster now in your IDE to switch between the two folders.
To see your stories, go to:
http://[yourLocalHost]/.stories. For the impatient, or for those who want to test drive, you can see the demo here:
As you can see, it’s fairly simple and straightforward, nothing too exciting, but that’s pretty much the point. The stories can be fully customized to one’s liking. Prefer Vuetify over Bootstrap? Not a problem! The key takeaway is that as a convenience to the developer, the routes that are needed are automatically generated as the stories get updated. The story routes are available in
this.$router.options.routes. If the developer is already used to creating pages in the “pages” folder, this should look and feel very familiar!
Important Notes & Known Quirks:
- The stories routes need a “stories root”. The module uses the first found “.vue” file in the “.stories” folder as the stories root. So, if you have both index.vue and a root.vue file in “.stories”, index.vue will be used, not the other. It seems to be convention that “index.vue” is used more commonly in the Nuxt community.
- Every future install of nuxt-stories will use “gently copy” so as not to overwrite all the stories you’ve worked on. When in doubt, commit your stories before updating.
- There may be a time when you wish to deploy your stories to a static website like I did (so that you can easily share them with stakeholders). The quick explanation is: view or copy my gh-pages branch to see how I configured things, and simply push your dist to your host (different hosts are easier to work with than others). The longer explanation is: choosing “.stories” as the stories folder is both a blessing and a curse. Files with a “.” prefix are generally treated by file systems as hidden. If you are in complete control of the server, you have no problem serving up the hidden files. However, other platforms will not include the hidden files, even if you pushed them. So, to avoid encountering the 404 errors, you’ll have to rename the “.stories” folder simply to something else, like “nuxtStories” or “stories”, just making sure not to conflict with any other real “stories” routes that your real app might use.
- ESLint may also ignore files or folders prefixed with “.”. If you are planning to include “.stories” as a directory to be linted, you need to simply “negate ignore” the directory in
.eslintignoreby adding the line:
With any luck, if I explained things correctly, you should now have storybooking enabled in your Nuxt application. In the past, writing and maintaining requirements has always been the daunting boring task no one wanted to be bothered with, but was always arguably the most important. When the requirements you write consist of functional components that get tested on save and hot reloaded in your actual app’s pages, you will get instant gratification that your app is well on its way to doing what you and your clients want (and hopefully in a time much shorter than what you expected).