Adding External Script to Specific Story in Storybook

Viewed 1005

I wanted to know how I can load in external javascript into a specific story in storybook. The only documentation I can find right now is how to do it globally https://storybook.js.org/docs/react/configure/story-rendering. Doing this works, i would just like to save on performance since only one of my stories uses an external js script.

1 Answers

No, there isn't a standard way to do this in storybook currently (version 6.5).

However you can achieve it with a decorator.

Depending on your needs it could look something like this (this is for a React story):

import { Story, Meta } from '@storybook/react';
import { useEffect } from '@storybook/addons';

export default {
  title: 'My Story',
  component: MyStory,
  decorators: [
    (Story) => {
      useEffect(() => {
        const script = document.createElement('script');
        script.src = '/my-script';
        document.body.appendChild(script);
      }, []);

      return <Story />;
    },
  ],
};

There are some caveats here though:

  1. The scripts will remain loaded as you navigate to other stories.
  2. The story will render before the script has loaded.

To handle these caveats you can:

  1. Add a cleanup handler to useEffect.
  2. Don't render your <Story/> until the script has loaded.

For example:

import { Story, Meta } from '@storybook/react';
import { useEffect, useState } from '@storybook/addons';

export default {
  title: 'My Story',
  component: MyStory,
  decorators: [
    (Story) => {
      const [isLoaded, setIsLoaded] = useState(false);
      useEffect(() => {
        const script = document.createElement('script');
        script.onload = () => {
          setIsLoaded(true);
        };
        script.src = '/my-script';
        document.body.appendChild(script);
        return () => {
          // clean up effects of script here
        };
      }, []);

      return isLoaded ? <Story /> : <div>Loading...</div>;
    },
  ],
};

If you have multiple scripts you'll have to wrap all the onload events into a Promise.all.

This could be wrapped up in an addon similar to storybook-addon-run-script.

Related