Storybook addons need a matching
@storybook/addons version. Here’s how to ensure it gets it.
Addons Need a Channel
In Storybook, you preview the components you’re writing with some nice surround UI to navigate and manipulate them. For this UI to communicate with the preview pane, there is a channel construct for pub/sub communication. Addons to Storybook utilize this channel.
This channel is accessible in this way:
import addons from '@storybook/addons' const channel = addons.getChannel()
When you’re writing a Storybook addon, things will feel just fine. It’s easy. It’s straightforward. But then you get the dreaded red screen announcing the problem that will eat up the next hours of your life:
Uncaught Error: Accessing nonexistent addons channel, see https://storybook.js.org/basics/faq/#why-is-there-no-addons-channel
The docs describe this error’s root cause as commonly being that you have two versions of
@storybook/addons. If there are multiple channel instances they cannot communicate together.
In my experience this solves the problem 99% of the time.
The other day, I fell right into the remaining 1% of the time where this didn’t work. I might have been doing it wrong, but no combination of uninstalling, reinstalling, version locking, version downgrading, or installation location changes would help. Finally, I got extreme.
One Version of Addons
I reasoned that if there is only one installation of
@storybook/addons, there will never be a version mismatch. So I go to the addon I built and uninstall all Storybook dependencies. Then I go to the component package I’m testing and install the needed Storybook dependencies:
npm install @storybook/react @storybook/addons
When I register my addon, I pass the
addon to it.
mycomponent/.storybook/addons.js looks something like:
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ import addons from '@storybook/addons' import register from 'mystorybook-addon/register' register(addons)
In this way, my addon’s
register.js doesn’t need to import
addons to call
addons.addPanel(). It uses the exact same version, even instance, of the
addons that my component uses.
When I use my addon (a decorator) in my component story, my
mycomponent/stories/index.js file looks something like:
import addons from '@storybook/addons' import myAddon from 'mystorybook-addon' storiesOf('some sort', module).addDecorator( myAddon(addons) )
Then my addon code internally has
addons available to it to call
addons.getChannel(). Problem solved.
But now my addon API looks a bit different than some of your average addons. But if this is such an issue and the singleton nature of the addons channel is so materially important, I wonder why this isn’t the default addon design.
Does any body else use another strategy to line up the
@storybook/addons version and the channel that isn’t simply to reinstall it?