A Storybook add-on for live editing stories. Supports React and TypeScript.

View on Github

storybook-addon-live-code-editor

A Storybook add-on for live editing stories. Supports React and TypeScript.

Demo

Usage example

Get started

Install as a dev dependency.

npm install --save-dev storybook-addon-live-code-editor

Add storybook-addon-live-code-editor in your .storybook/main.js file and add the staticDirs:

// .storybook/main.js
const {
  getCodeEditorStaticDirs
} = require('storybook-addon-live-code-editor/getStaticDirs');

module.exports = {
  addons: [
    'storybook-addon-live-code-editor',
    ...
  ],
  staticDirs: [
    ...getCodeEditorStaticDirs(),
    ...

staticDirs sets a list of directories of static files to be loaded by Storybook. The editor (monaco-editor) requires these extra static files to be available at runtime.

Additional static files can be added using the getExtraStaticDir helper from storybook-addon-live-code-editor/getStaticDirs:

// .storybook/main.js
const {
  getCodeEditorStaticDirs,
  getExtraStaticDir,
} = require('storybook-addon-live-code-editor/getStaticDirs');

module.exports = {
  staticDirs: [
    ...getCodeEditorStaticDirs(),
    getExtraStaticDir('monaco-editor/esm'), // hosted at: monaco-editor/esm
    ...

Important:

The default Webpack 4 builder does not work with storybook-addon-live-code-editor. Please use one of the following:

API

Playground

Use the Playground component in MDX format.

// MyComponent.stories.mdx
import { Playground } from 'storybook-addon-live-code-editor';

<Playground code="export default () => <h1>Hello</h1>;"} />
// MyComponent.stories.mdx
import { Playground } from 'storybook-addon-live-code-editor';
import * as MyLibrary from './index';
import storyCode from './MyStory.source.tsx?raw';
import MyLibraryTypes from '../dist/types.d.ts?raw';

<Playground
  availableImports={{ 'my-library': MyLibrary }}
  code={storyCode}
  height="560px"
  id="unique id used to save edited code until the page is reloaded"
  modifyEditor={(monaco, editor) => {
    // editor docs: https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneCodeEditor.html
    // monaco docs: https://microsoft.github.io/monaco-editor/api/modules/monaco.html
    editor.getModel().updateOptions({ tabSize: 2 });
    monaco.editor.setTheme('vs-dark');
    monaco.languages.typescript.typescriptDefaults.addExtraLib(
      MyLibraryTypes,
      'file:///node_modules/my-library/index.d.ts'
    );
  }}
/>;

Playground props:

interface PlaygroundProps {
  availableImports?: {
    [importSpecifier: string]: {
      [namedImport: string]: any;
    };
  };
  code?: string;
  height?: string;
  id?: string | number | symbol;
  modifyEditor?: (monaco: Monaco, editor: Monaco.editor.IStandaloneCodeEditor) => any;
}

React is automatically imported if code does not import it. React TypeScript definitions will be automatically loaded if @types/react is available.

createStory

Use the createStory function in traditional stories:

// MyComponent.stories.js
import { createStory } from 'storybook-addon-live-code-editor';
import * as MyLibrary from './index';
import storyCode from './MyStory.source.tsx?raw';

export const MyStory = createStory({
  availableImports: { 'my-library': MyLibrary },
  code: storyCode,
});

createStory options:

interface CreateStoryOptions {
  availableImports?: {
    [importSpecifier: string]: {
      [namedImport: string]: any;
    };
  };
  code: string;
  modifyEditor?: (monaco: Monaco, editor: Monaco.editor.IStandaloneCodeEditor) => any;
}

setupMonaco

setupMonaco allows customization of monaco-editor.

Use this in your .storybook/preview.js to add type definitions or integrations.

Check out examples of monaco-editor with different configurations.

// .storybook/preview.js
import { setupMonaco } from 'storybook-addon-live-code-editor';

setupMonaco({
  // https://microsoft.github.io/monaco-editor/typedoc/interfaces/Environment.html
  monacoEnvironment: {
    getWorker(moduleId, label) {
      ...
    },
  },
  // onMonacoLoad is called when monaco is first loaded and before an editor instance is created.
  onMonacoLoad(monaco) {
    ...
  },
});

setupMonaco options:

interface MonacoSetup {
  monacoEnvironment?: Monaco.Environment;
  onMonacoLoad?: (monaco: Monaco) => any;
}

Contributing

Install dependencies

npm install

Run example

npm run install-example-deps
npm run start

Run tests

npm run test

Format code

npm run format

Build library

npm run build

Commits

Use conventional commits to allow automatic versioned releases.

  • fix: represents bug fixes, and correlates to a SemVer patch.
  • feat: represents a new feature, and correlates to a SemVer minor.
  • feat!:, or fix!:, refactor!:, etc., represent a breaking change (indicated by the !) and will result in a SemVer major.

Publishing

The automated release-please PR to the main branch can be merged to deploy a release.