DEV Community

Cover image for React Native and React Native Web Storybook together
Danny
Danny

Posted on

React Native and React Native Web Storybook together

Storybook for react native allows you to develop your components in isolation on their native platforms, but its not the easiest to share with stakeholders or team members. Running your components on the web using react native web can be a nice way to make your components more shareable and you can integrate with lots of web tooling built around storybook for the web. It's possible to merge the setup for react native storybook together with the react native web storybook.

Its not been the easiest setup process for new users so I've made this quick guide for those with an existing project wanting to integrate @storybook/react-native and @storybook/addon-react-native-web in the same project.

If you are creating a fresh project it would be easier to start with one of the templates I've already created.

I'll be starting with a basic expo template generated with yarn create expo-app

Ondevice setup

Now to add @storybook/react-native I'll run the following command:
npx sb@latest init --type react_native

Then in my App.js file I'll make some changes to import the storybook ui from .storybook and export it as the the entry point of the app.

// App.js
import StorybookUI from "./.storybook";

export default StorybookUI;
Enter fullscreen mode Exit fullscreen mode

Now I'm going to customize metro config to complete the setup

This command will generate the config file: npx expo customize metro.config.js

// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");

const config = getDefaultConfig(__dirname);

config.resolver.resolverMainFields.unshift("sbmodern");

module.exports = config;
Enter fullscreen mode Exit fullscreen mode

Add a folder called components and lets add a basic component called Button

// components/Button.js
import { Text, TouchableOpacity } from "react-native";

export const Button = ({ onPress, text }) => {
  return (
    <TouchableOpacity
      onPress={onPress}
      style={{
        borderColor: "lightgrey",
        borderWidth: 1,
        borderRadius: 2,
        paddingVertical: 8,
        paddingHorizontal: 32,
        alignSelf: "flex-start",
      }}
    >
      <Text>{text}</Text>
    </TouchableOpacity>
  );
};
Enter fullscreen mode Exit fullscreen mode
// components/Button.stories.js
import { Button } from "./Button";

export default {
  title: "Button",
  component: Button,
  argTypes: {
    onPress: { action: "onPress" },
  },
};

export const Base = {
  args: { text: "Hello World" },
};
Enter fullscreen mode Exit fullscreen mode

lets update the main.js config file to look for stories in this folder

// .storybook/main.js
module.exports = {
  stories: ["../components/**/*.stories.?(ts|tsx|js|jsx)"],
  addons: [
    "@storybook/addon-ondevice-controls",
    "@storybook/addon-ondevice-actions",
  ],
};
Enter fullscreen mode Exit fullscreen mode

Lets also update the preview.js to add some padding to our stories

// .storybook/preview.js
import { View } from "react-native";

export const parameters = {};

export const decorators = [
  (Story) => (
    <View style={{ padding: 8 }}>
      <Story />
    </View>
  ),
];
Enter fullscreen mode Exit fullscreen mode

Next I'll run storybook-generate to make sure the storybook.requires.js file has been generated.

yarn storybook-generate
Enter fullscreen mode Exit fullscreen mode

This file is generated based on your config and handles importing all the necessary story files.

This concludes the on-device part of the setup. You should be able to run yarn ios or yarn android and see storybook render your button story.

Image description

Web setup

The best react-native-web Storybook experience will come from using @storybook/addon-react-native-web on top of a ReactJS storybook setup. This is a slightly more complex setup because it requires you to have two sets of storybook config and scripts. However those tradeoffs give you the ability to use all the features of web storybook and tooling that comes with it.

Since React Native Storybook is currently at compatibility with 6.5 we'll be using that version for both web and on-device.

First install the dependencies

yarn add -D @storybook/addon-react-native-web @storybook/addon-essentials@^6.5 @storybook/builder-webpack5@^6.5 @storybook/manager-webpack5@^6.5 @storybook/react@^6.5 babel-plugin-react-native-web react-native-web @babel/plugin-proposal-export-namespace-from
Enter fullscreen mode Exit fullscreen mode

Now add the scripts to package.json

"storybook:web": "start-storybook -p 6006 -c .storybook-web",
"build-storybook": "build-storybook -c .storybook-web"
Enter fullscreen mode Exit fullscreen mode

Create a new folder called .storybook-web and add the files main.js and preview.js

// ./storybook-web/main.js
module.exports = {
  stories: [
    "../components/**/*.stories.mdx",
    "../components/**/*.stories.@(js|jsx|ts|tsx)",
  ],
  addons: [
    "@storybook/addon-essentials",
    "@storybook/addon-react-native-web",
  ],
  core: {
    builder: "webpack5",
  },
  framework: "@storybook/react",
};
Enter fullscreen mode Exit fullscreen mode
// ./storybook-web/preview.js
import { View } from "react-native";

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
};

export const decorators = [
  /* by wrapping everything in a view the parent container is always using flexbox and behaves like a react-native-web component would  */
  (Story) => (
    <View>
      <Story />
    </View>
  ),
];
Enter fullscreen mode Exit fullscreen mode

Now if you run yarn storybook:web you should see something like this.

Image description

Likely this won't be the end of the configuring and setup you'll need to do but this should get you most of the way.

Thanks for reading.

I'm on twitter at @Danny_H_W and github at dannyhw

Top comments (1)

Collapse
 
joevaugh4n profile image
Joe

<3 this, Danny