Docs
Storybook Docs

Addon API

Core Addon API

This is the core addon API. This is how to get the addon API:

.storybook/my-addon/manager.js
import { addons } from '@storybook/addons';

addons.getChannel()

Get an instance to the channel to communicate with the manager and the preview. You can find this in both the addon register code and your addon’s wrapper component (where used inside a story).

It has a NodeJS EventEmitter compatible API. So, you can use it to emit events and listen for events.

addons.register()

This method allows you to register an addon and get the storybook API. You can do this only in the Manager App. See how we can use this:

.storybook/my-addon/manager.js
import { addons } from '@storybook/addons';
 
// Register the addon with a unique name.
addons.register('my-organisation/my-addon', (api) => {});

Now you'll get an instance to our StorybookAPI. See the api docs for Storybook API regarding using that.

addons.add()

This method allows you to add a panel to Storybook. (Storybook's Action Logger is a panel). You can do this only in the Manager App. See how you can use this method:

.storybook/my-addon/manager.js
import React from 'react';
 
import { addons, types } from '@storybook/addons';
 
import { AddonPanel } from '@storybook/components';
 
const ADDON_ID = 'myaddon';
const PANEL_ID = `${ADDON_ID}/panel`;
 
// give a unique name for the panel
const MyPanel = () => <div>MyAddon</div>;
 
addons.register(ADDON_ID, (api) => {
  addons.add(PANEL_ID, {
    type: types.PANEL,
    title: 'My Addon',
    render: ({ active, key }) => (
      <AddonPanel active={active} key={key}>
        <MyPanel />
      </AddonPanel>
    ),
  });
});

The render function is called with active and key. The active value will be true when the panel is focused on the UI.

As you can see, you can set any React Component as the panel. Currently, it's one line of text. But you can do anything you want. It's a good practice to specify the panel title with the title key. You can use any plain text with it.

makeDecorator API

Use the makeDecorator API to create decorators in the style of the official addons. Like so:

.storybook/my-addon/manager.js
import { makeDecorator } from '@storybook/addons';
 
export makeDecorator({
  name: 'withSomething',
  parameterName: 'something',
  wrapper: (storyFn, context, { parameters }) => {
    // Do something with `parameters`, which are set via { something: ... }
 
    // Note you may alter the story output if you like. 
    // Although generally that's not advised.
 
    return storyFn(context);
  }
})

The options to makeDecorator are:

  • name: The name of the export (e.g. withFoo)
  • parameterName: The name of the parameter your addon uses. This should be unique.
  • skipIfNoParametersOrOptions: Don't run your decorator if the user hasn't set options via:
    • .addDecorator(withFoo(options))
    • parameters with .add('story', () => <Story/>, { foo: 'param' }), or .addParameters({ foo: 'param' })
  • allowDeprecatedUsage: support the deprecated "wrapper" usage (.add('story', () => withFoo(options)(() => <Story/>))).
  • wrapper: your decorator function. Takes the storyFn, context, and both the options and parameters (as defined in skipIfNoParametersOrOptions above).

If the story's parameters include { foo: { disable: true } } (where foo is the parameterName of your addon), your decorator will not be called.


Storybook hooks

Writing addons can be simplified a lot by using these Storybook hooks:

useStorybookState

/my-addon/manager.js
import React from 'react';
 
import { useStorybookState } from '@storybook/api';
 
export const Panel = () => {
  const state = useStorybookState();
 
  return <div>do something with storybook's state</div>;
};

It allows full access to the entire storybook state. Your component will re-render whenever the storybook state changes.

If you use this, remember your component will be re-rendered a lot, and you may need to optimize for that using React.memo or useMemo or PureComponent.

useStorybookApi

/my-addon/manager.js
import React from 'react';
 
import { useStorybookApi } from '@storybook/api';
 
export const Panel = () => {
  const state = useStorybookApi();
 
  return <div>do something with storybook's api</div>;
};

It allows full access to the storybook API.

Details on the Storybook API are further down.

useChannel

/my-addon/manager.js
import React from 'react';
 
import { STORY_CHANGED } from '@storybook/core-events';
 
export const Panel = () => {
  const emit = useChannel({
    STORY_CHANGED: (...args) => console.log(...args),
  });
 
  return (
    <button onClick={() => emit('my-event-type', { some: 'data' })}>
      clicking this will emit an event
    </button>
  );
};

Allows both setting subscriptions to events and getting the emitter for emitting custom events to the channel.

The messages can be listened to on both the iframe and the manager side.

useAddonState

/my-addon/manager.js
import React from 'react';
 
import { useAddonState } from '@storybook/api';
 
export const Panel = () => {
  const [state, setState] = useAddonState('my/addon-id', 'initial state');
 
  return <button onClick={() => setState('a new value')}>the state = "{state}"</button>;
};
export const Tool = () => {
  const [state, setState] = useAddonState('my/addon-id', 'initial state');
 
  return <button onClick={() => setState('a new value')}>the state = "{state}"</button>;
};

Extremely useful for addons that need to persist in some form of state.

Storybook may unmount your addon component, so keeping local state might not work well.

Also, some addons consist of multiple parts, some parts in a panel, some in the toolbar, etc.

With this hook, addons can access the same portion of the state, persisted even if the components are unmounted.

useParameter

/my-addon/manager.js
import React from 'react';
 
import { useParameter } from '@storybook/api';
 
export const Panel = () => {
  const value = useParameter('parameter-key', 'default value');
 
  return (
    <div>
      for the currently selected story, the parameter for "parameter-key" is:
      {value}
    </div>
  );
};

This hook gets you the current story's parameter.

If the parameter isn't set, the default value (second argument) is returned instead.

useGlobals

/my-addon/manager.js
import React from 'react';
 
import { useGlobals } from '@storybook/api';
 
export const Panel = () => {
  const [globals, updateGlobals] = useGlobals();
 
  const isActive = globals['my-param-key'] || false;
 
  return (
    <button onClick={() => updateGlobals({ ['my-param-key']: !isActive })}>
      {isActive ? 'Hide me!' : 'Show me!'}
    </button>
  );
};

Extremely useful hook for addons that rely on Storybook Globals.

It allows you to retrieve and update any Storybook Globals you want.

If you use this hook, remember that your component will render a lot, and you may need to optimize for that using React.memo or useMemo or useCallback.

useArgs

your-addon/manager.js
import { useArgs } from '@storybook/api';
 
const [args, updateArgs, resetArgs] = useArgs();
 
// To update one or more args:
updateArgs({ key: 'value' });
 
// To reset one (or more) args:
resetArgs((argNames: ['key']));
 
// To reset all args
resetArgs();

You can use this handy Storybook hook in your addon if you need to read or update args.


Storybook API

Storybook API allows you to access different functionalities of Storybook UI. You can move an instance to the Storybook API when registering an addon.

Let's have a look at API methods.

api.selectStory()

With this method, you can select a story via an API. This method accepts two parameters.

  1. story kind name
  2. story name (optional)

Let's say you've got a story like this:

Button.stories.ts|tsx
import React from 'react';
 
import { ComponentStory, ComponentMeta } from '@storybook/react';
 
import { Button } from './Button';
 
export default {
  /* πŸ‘‡ The title prop is optional.
  * See https://storybook.js.org/docs/6/configure#configure-story-loading
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: Button,
  //πŸ‘‡ Creates specific parameters for the story
  parameters: {
    myAddon: {
      data: 'this data is passed to the addon',
    },
  },
} as ComponentMeta<typeof Button>;
 
const Basic: ComponentStory<typeof Button> = () => <Button>hello</Button>;

This is how you can select the above story:

/my-addon/manager.js
addons.register('my-organisation/my-addon', (api) => {
  api.selectStory('Button', 'Basic');
});

api.selectInCurrentKind()

Same as selectStory, but accepts a story inside current kind as the only parameter:

/my-addon/manager.js
addons.register('my-organisation/my-addon', (api) => {
  api.selectInCurrentKind('Basic');
});

api.setQueryParams()

This method allows you to set query string parameters. You can use that as temporary storage for addons. Here's how you define query params:

/my-addon/manager.js
addons.register('my-organisation/my-addon', (api) => {
  api.setQueryParams({
    abc: 'this is abc',
    bbc: 'this is bbc',
  });
});

If you need to remove a query param, use null for that. For example, we need to remove the bbc query param. See below how to do it:

/my-addon/manager.js
addons.register('my-organisation/my-addon', (api) => {
  api.setQueryParams({
    bbc: null,
  });
});

api.getQueryParam()

This method allows you to get a query param set by the above API setQueryParams. For example, we need to get the bbc query param. Then this is how we do it:

/my-addon/manager.js
addons.register('my-organisation/my-addon', (api) => {
  api.getQueryParam('bbc');
});

api.getUrlState(overrideParams)

This method allows you to get the application URL state with some changed params. For example, if you want to get a link to a particular story:

/my-addon/manager.js
addons.register('my-organisation/my-addon', (api) => {
  const href = api.getUrlState({
    selectedKind: 'kind',
    selectedStory: 'story',
  }).url;
});

api.on(eventName, fn)

This method allows you to register a handler function called whenever the user navigates between stories.

/my-addon/manager.js
addons.register('my-organisation/my-addon', (api) => {
  api.on('some-event', (eventData) => console.log(eventData));
});

addons.setConfig(config)

This method allows you to override the default Storybook UI configuration (e.g., set up a theme or hide UI elements):

.storybook/manager.js
import { addons } from '@storybook/addons';
 
addons.setConfig({
  isFullscreen: false,
  showNav: true,
  showPanel: true,
  panelPosition: 'bottom',
  enableShortcuts: true,
  showToolbar: true,
  theme: undefined,
  selectedPanel: undefined,
  initialActive: 'sidebar',
  sidebar: {
    showRoots: false,
    collapsedRoots: ['other'],
  },
  toolbar: {
    title: { hidden: false },
    zoom: { hidden: false },
    eject: { hidden: false },
    copy: { hidden: false },
    fullscreen: { hidden: false },
  },
});

The following table details how to use the API values:

NameTypeDescriptionExample Value
isFullscreenBooleanShow story component as full screenfalse
showNavBooleanDisplay panel that shows a list of storiestrue
showPanelBooleanDisplay panel that shows addon configurationstrue
panelPositionString/ObjectWhere to show the addon panelbottom or right
enableShortcutsBooleanEnable/disable shortcutstrue
showToolbarBooleanShow/hide tool bartrue
themeObjectStorybook Theme, see next sectionundefined
selectedPanelStringId to select an addon panelstorybook/actions/panel
initialActiveStringSelect the default active tab on Mobilesidebar or canvas or addons
sidebarObjectSidebar options, see below{ showRoots: false }
toolbarObjectModify the tools in the toolbar using the addon id{ fullscreen: { hidden: false } }

The following options are configurable under the sidebar namespace:

NameTypeDescriptionExample Value
showRootsBooleanDisplay the top-level nodes as a "root" in the sidebarfalse
collapsedRootsArraySet of root node IDs to visually collapse by default['misc', 'other']
renderLabelFunctionCreate a custom label for tree nodes; must return a ReactNode(item) => <abbr title="...">{item.name}</abbr>

The following options are configurable under the toolbar namespace:

NameTypeDescriptionExample Value
idStringToggle visibility for toolbar item{ hidden: false }