Skip to main content
This page covers migration guides for older versions of Storybook React Native.

From version 7.6.x to 8.3.x

In this version of storybook we’ve reworked the UI using some community react native packages. We’ve also overhauled the theme to match the web version.
1
Update dependencies
2
Update all storybook dependencies to 8.3.5 or newer.
3
For example you may end up with something like this:
4
{
  "devDependencies": {
    "@storybook/react-native": "^8.3.5",
    "@storybook/react": "^8.3.5",
    "@storybook/addon-ondevice-controls": "^8.3.5",
    "@storybook/addon-ondevice-actions": "^8.3.5",
    "@storybook/addon-ondevice-backgrounds": "^8.3.5",
    "@storybook/addon-ondevice-notes": "^8.3.5"
  }
}
5
Add the new required dependencies to your project:
6
Expo:
7
npx expo install react-native-reanimated react-native-gesture-handler @gorhom/bottom-sheet react-native-svg
8
RN CLI:
9
npm install react-native-reanimated react-native-gesture-handler @gorhom/bottom-sheet react-native-svg
10
Make sure you use versions of those packages supported by your version of expo/react-native.
11
Regenerate your requires file
12
Regenerate your .storybook/storybook.requires.ts file by running:
13
yarn storybook-generate
14
You should see a new updateView function in the file.
15
Update your metro config
16
// metro.config.js
const path = require('path');
const withStorybook = require('@storybook/react-native/metro/withStorybook');

module.exports = withStorybook(config, {
  // set to false to disable storybook specific settings
  // you can use a env variable to toggle this
  enabled: true,
  // path to your storybook config folder
  configPath: path.resolve(__dirname, './.storybook'),
});

From version 6.5.x to 7.6.x

In this version of storybook we’ve made a lot of changes to the internals of react native storybook to make it more compatible with core storybook libraries. This means compatibility with the new v7 store and the API changes that comes with that. Here are some of the improvements:
  • New storage option that lets you choose what storage solution you want to use (async storage/mmkv etc)
  • Support for main.ts
  • Dynamic imports enabled by the unstable_allowRequireContext option in metro config
    • You only need to generate your requires file when main.ts changes
  • Error boundaries for stories so your app shouldn’t crash when a story throws an error
  • Improved markdown renderer for notes addon
  • Simpler setup for auto args
You should follow a different set of changes if you need to support storiesOf, see the storiesOf support section below.
1
Update dependencies
2
Update all storybook dependencies to 7.6.10 or newer:
3
{
  "@storybook/react-native": "^7.6.10",
  "@storybook/addon-ondevice-actions": "^7.6.10",
  "@storybook/addon-ondevice-backgrounds": "^7.6.10",
  "@storybook/addon-ondevice-controls": "^7.6.10",
  "@storybook/addon-ondevice-notes": "^7.6.10"
}
4
Regenerate your requires file
5
Regenerate your storybook.requires file by running:
6
yarn storybook-generate
7
It should now generate a storybook.requires.ts file instead of a storybook.requires.js file. This provides the type for the new view export.
8
Update .storybook/index.js
9
Update .storybook/index.js to use the new getStorybookUI function on the view exported from storybook.requires.ts. You can also change this file to be called .storybook/index.tsx.
10
You should also now pass a storage object to the getStorybookUI function. This is used to persist the selected story between reloads.
11
// .storybook/index.tsx
import { view } from './storybook.requires';
import AsyncStorage from '@react-native-async-storage/async-storage';

const StorybookUIRoot = view.getStorybookUI({
  storage: {
    getItem: AsyncStorage.getItem,
    setItem: AsyncStorage.setItem,
  },
});

export default StorybookUIRoot;
12
Update metro config
13
Update your metro.config.js to enable the unstable_allowRequireContext option and you can now remove the sbmodern resolver if you have it.
14
The unstable_allowRequireContext option requires at least react native 0.72.
15
If you are using expo and you don’t have a metro config file you can create one by running:
16
npx expo customize metro.config.js
17
You can also add here the generate function to automatically update the storybook.requires.ts file when you start metro. You only need to regenerate this file now when main.js updates since requireContext allows us to use dynamic imports.
18
Expo:
19
// metro.config.js
const path = require('path');
const { getDefaultConfig } = require('expo/metro-config');
const { generate } = require('@storybook/react-native/scripts/generate');

generate({
  configPath: path.resolve(__dirname, './.storybook'),
});

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

config.transformer.unstable_allowRequireContext = true;
config.resolver.sourceExts.push('mjs');

module.exports = config;
20
React Native CLI:
21
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const path = require('path');
const { generate } = require('@storybook/react-native/scripts/generate');

generate({
  // update ./.storybook to your storybook folder
  configPath: path.resolve(__dirname, './.storybook'),
});

const defaultConfig = getDefaultConfig(__dirname);

/**
 * Metro configuration
 * https://facebook.github.io/metro/docs/configuration
 *
 * @type {import('metro-config').MetroConfig}
 */
const config = {
  transformer: {
    unstable_allowRequireContext: true,
  },
  resolver: {
    sourceExts: [...defaultConfig.resolver.sourceExts, 'mjs'],
  },
};

module.exports = mergeConfig(defaultConfig, config);
22
You can now also remove anything from package.json scripts which would run generate before running storybook.
23
Update types
24
We’ve removed the types from @storybook/react-native and now you should import them from @storybook/react. This is to remove duplication and increase compatibility with core storybook libraries.
25
Here’s an example story in version 7:
26
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta = {
  component: Button,
  argTypes: {
    onPress: { action: 'pressed the button' },
  },
} satisfies Meta<typeof Button>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Basic: Story = {
  args: {
    text: 'Press me!',
  },
};
27
You can now also update main.js to main.ts and use the StorybookConfig type. This is one of the only types we export from @storybook/react-native in this version.
28
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-native';

const main: StorybookConfig = {
  stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
  addons: [
    '@storybook/addon-ondevice-notes',
    '@storybook/addon-ondevice-controls',
    '@storybook/addon-ondevice-backgrounds',
    '@storybook/addon-ondevice-actions',
  ],
};

export default main;
29
To update preview.js to preview.tsx you can use the Preview type from @storybook/react:
30
import type { Preview } from '@storybook/react';

const preview: Preview = {
  decorators: [],
  parameters: {},
};

export default preview;
31
Remove manual doctools setup
32
If you are manually adding doctools to do auto args you can now remove this code since its automatically added now. To make it work you still need babel-plugin-react-docgen-typescript though.
33
-import { extractArgTypes } from "@storybook/react/dist/modern/client/docs/extractArgTypes";
-import { addArgTypesEnhancer, addParameters } from "@storybook/react-native";
-import { enhanceArgTypes } from "@storybook/core/docs-tools";

-addArgTypesEnhancer(enhanceArgTypes);
-addParameters({
-  docs: {
-    extractArgTypes,
-  },
-});

6.5.x to 7.6.x with storiesOf support

Storybook v7 doesn’t support storiesOf in the same way as v6, but there is a compatibility mode that allows you to continue using storiesOf whilst you migrate your stories.
By opting into this compatibility mode you will lose out on some features of v7. We highly recommend moving to CSF stories.StoriesOf will be removed in v8 along with knobs and testing on those deprecated features will be limited.
1
Update dependencies
2
Update all storybook dependencies to 7.6.10 or newer:
3
{
  "@storybook/react-native": "^7.6.10",
  "@storybook/addon-ondevice-actions": "^7.6.10",
  "@storybook/addon-ondevice-backgrounds": "^7.6.10",
  "@storybook/addon-ondevice-controls": "^7.6.10",
  "@storybook/addon-ondevice-notes": "^7.6.10"
}
4
Update your package.json scripts
5
To opt in you can pass —v6-store to sb-rn-get-stories in the generate script:
6
{
  "scripts": {
    "storybook-generate": "sb-rn-get-stories --v6-store"
  }
}
7
You should also now import storiesOf from @storybook/react-native/V6 this is necessary so that certain code paths don’t run in v7 mode.
8
Regenerate your requires file
9
Now that you’ve updated the script you can regenerate your storybook.requires.js file by running:
10
yarn storybook-generate
11
It should now have the updated @storybook/react-native/V6 import in it.
12
Update .storybook/index.js
13
Update the import in .storybook/index.js from @storybook/react-native to @storybook/react-native/V6. You can also change this file to be called .storybook/index.tsx.
14
You should also make sure to add the storage prop to the getStorybookUI call. This lets you opt into using a different storage solution like mmkv or if you put async storage there it will continue to work as it did before.
15
// .storybook/index.tsx
import './storybook.requires';
import { getStorybookUI } from '@storybook/react-native/V6';
import AsyncStorage from '@react-native-async-storage/async-storage';

const StorybookUIRoot = getStorybookUI({
  storage: {
    getItem: AsyncStorage.getItem,
    setItem: AsyncStorage.setItem,
  },
});

export default StorybookUIRoot;
16
Update your stories
17
Where ever you use storiesOf you should now import it from @storybook/react-native/V6:
18
import { storiesOf } from '@storybook/react-native/V6';
import { text } from '@storybook/addon-knobs';
import { withKnobs } from '@storybook/addon-ondevice-knobs';
import React from 'react';
import { Button } from './AnotherButton';

storiesOf('Another Button', module)
  .addDecorator(withKnobs)
  .add('another button example', () => <Button text={text('text', 'test2')} onPress={() => null} />)
  .add('again', () => <Button text={text('text', 'text2')} onPress={() => null} />);
19
Update types
20
If you’re using typescript you may notice that for v7 we’ve removed the types in @storybook/react-native. That’s part of an effort to re-use more code, you should now import them from @storybook/react instead.

From version 5.3.x to 6.5.x

1
Install additional dependencies
2
To make storybook more compatible with core storybook libraries we are using some new dependencies. You will need to add these to your project:
3
yarn add -D @storybook/react-native @storybook/core/common @react-native-async-storage/async-storage react-native-safe-area-context react-dom
4
Controls (the new knobs):
5
To use the controls addon you will need these dependencies:
6
yarn add -D @storybook/addon-ondevice-controls @storybook/addons @storybook/addon-controls @react-native-community/datetimepicker @react-native-community/slider
7
Actions:
8
To use the actions addon you will need these dependencies:
9
yarn add -D @storybook/addon-ondevice-actions @storybook/addon-actions
10
Rename storybook folder
11
Rename the storybook folder to .storybook.
12
Update your index.js file
13
In 6.5 we use a script to generate your imports for stories and addons. This uses the new main.js file to generate the storybook.requires.js file. This file is then imported into the index.js file.
14
Remove the configure call, story imports and addon imports and reduce the index file to have this content. The most important thing is to import the storybook.requires file.
15
Also if your Storybook folder is called storybook you should change it to .storybook.
16
// .storybook/index.js
import { getStorybookUI } from '@storybook/react-native';
import './storybook.requires';

const StorybookUIRoot = getStorybookUI({
  // options go here
});

export default StorybookUI;
17
Add main.js and preview.js
18
In your .storybook folder add the main.js and preview.js files.
19
In the stories field of your main.js file update the regex to work for your project.
20
In the addons field list the addons you want to use, these must be compatible with React Native Storybook. Addons made for React Native are usually prefixed with addon-ondevice.
21
// .storybook/main.js
module.exports = {
  stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
  addons: [
    '@storybook/addon-ondevice-controls',
    '@storybook/addon-ondevice-actions',
    '@storybook/addon-ondevice-backgrounds',
  ],
};
22
Move any global decorators and parameters you have to preview.js, if you don’t have any then just export an empty array for decorators and an empty object for parameters.
23
// .storybook/preview.js
import { View } from 'react-native';

export const decorators = [
  // Using a decorator to apply padding for every story
  (StoryFn) => (
    <View style={{ flex: 1, padding: 8 }}>
      <StoryFn />
    </View>
  ),
];

export const parameters = {
  my_param: 'anything',
};
24
Update package.json scripts
25
In storybook 6.5 there are some new binaries that generate your story imports and one that watches for new files.
26
You can add these scripts to your package.json file:
27
{
  "scripts": {
    "storybook-generate": "sb-rn-get-stories",
    "storybook-watch": "sb-rn-watcher"
  }
}
28
You’ll want to run the generate script whenever you add a new story file. Alternatively you can keep the watcher running.
29
Update your metro config
30
We use the sbmodern resolver field in order to resolve the modern version of storybook packages. Doing this removes the polyfills that ship in commonjs modules and fixes multiple long standing issues such as the promises never resolving bug and more (caused by corejs promises polyfill).
31
Expo:
32
First create metro config file if you don’t have it yet:
33
npx expo customize metro.config.js
34
Then add sbmodern to the start of the resolver.resolverMainFields list:
35
// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');

--module.exports = getDefaultConfig(__dirname);
++const defaultConfig = getDefaultConfig(__dirname);

++defaultConfig.resolver.resolverMainFields.unshift('sbmodern');

++module.exports = defaultConfig;
36
React Native CLI:
37
module.exports = {
  /* existing config */
  resolver: {
    resolverMainFields: ['sbmodern', 'react-native', 'browser', 'main'],
  },
};
38
Convert your stories to CSF
39
Whilst storiesOf will still work it is now deprecated so we recommend that you convert your stories to CSF.
40
There is a codemod for your convenience which should automatically make the code changes for you (make sure to update the glob to fit your files):
41
npx storybook@next migrate storiesof-to-csf --glob="src/**/*.stories.tsx"
42
Replace the storiesOf API with a default export that defines the title and component of your stories. Replace the add method with named exports that define each story. If you have any parameters or decorators, you can add them to the default export or to stories.
43
Before:
44
storiesOf('Button', module)
  .addParameters({ myParam: "anything" })
  .addDecorator((Story)=> <Wrapper>{Story}</Wrapper>)
  .add('primary', () => <Button primary label="Button" />)
  .add('secondary', () => <Button label="Button" />);
45
After:
46
export default {
  title: 'Button',
  component: Button,
  // for all stories in this file
  parameters: { myParam: "anything" },
};

export const Primary = { args: { primary: true, label: "button" } };

export const Secondary = {
  // this gives the property "label" the default value "button"
  args: { label: "button" },
  // for just this story
  decorators: [(Story) => (<Wrapper><Story/></Wrapper>)],
  parameters: { myParam: "something else" },
};

Build docs developers (and LLMs) love