DEV Community

Cover image for Step 3: Setting up Storybook with React Native Web: show your mobile components the browser!
Carl-W
Carl-W

Posted on

Step 3: Setting up Storybook with React Native Web: show your mobile components the browser!

In the last part of this series it's finally time to use what we installed in the first two parts and use everything together!

If you have not been following along please go and have a look at the first parts in this series!

Article Link
setup react native & @storybook/react-native Step 1: Setting up React Native with Storybook
setup react from scratch together with react native web Step 2: Setting up react with react native web
setup @storybook/react + react native web to run as a parallel storybook You are here now!

Starting point

To just do a quick recap I want to state where we are at at this point in our journey.

  • After Step 1, was completed we had a running React Native project with Storybook installed. It means when we run the code we have a storybook installation, which listens to the storybook development server for react native. Further we have react-native-storyloader set up. It loads our Storybook stories files for us when we run the dev command.
  • After Step 2, we have in parallel a detached vanilla React project set up, with it's own webpack configuration, which is also using react native web.

So! what do we do now?! ๐Ÿคทโ€โ™‚๏ธ

Manually installing Storybook for React.js ๐Ÿคธโ€โ™‚๏ธ

For our repos web alter ego React.js installation we need to install Storybook, and since this was initiated as a React Native project we need to do that manually.

It's pretty straight forward and is described well in the storybook docs here: Storybook Docs: React.js Guide. Edit: WAS well described They changes the docs....

Let's go through the steps:

1.) at the root run the command in your terminal: ๐Ÿš€

$ npx sb init --type react -f
Enter fullscreen mode Exit fullscreen mode
  • --type react tells the Storybook CLI to install stuff for a react project
  • -f Forces the installation, because the CLI will detect the react native installation and abort the installation without the face flag.

If everything completes properly you will see a newly created .storybook folder in the root of you project and a .stories folder added to your .src folder. Further it added a couple of scripts, and @storybook/react + react-is packages was installed + added to your devDependencies in your package.json.

2.) Add our scripts to package.json ๐Ÿš€

It's a habit of mine to add scripts first, because it reminds me of what I'm trying to accomplish. All this work is because I want to run a script and something should happen right.

The Storybook CLI might overwrite some of the scripts already present in your package.json, I fiddled around a bit and landed on this final version for my scripts:

  "scripts": {
    "android": "yarn run prestorybook && react-native run-android",
    "ios": "yarn run prestorybook && react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint .",
    "prestorybook": "rnstl",
    "storybook": "start-storybook -p 7007",
    "build-react": "webpack --mode production",
    "start-react": "webpack-dev-server --config ./webpack.config.js --mode development",
    "start-storybook-web": "./node_modules/@storybook/react/bin/index.js",
    "build-storybook-web": "./node_modules/@storybook/react/bin/build.js",
    "storybook-web": "yarn run start-storybook-web",
  },
Enter fullscreen mode Exit fullscreen mode

The one we are focusing on right now are the start-storybook-web, build-storybook-web and storybook-web. The previous scripts we covered in the first two steps in the series.

3.) [Optional] Test our React.js Storybook installation before modifying it. ๐Ÿš€

At this point we already have React Native component(s) inside of src/components and they cannot be rendered by Storybook as it is right now. To see that error in action you can right now run the script, by typing this command in your terminal:

$ yarn start-storybook-web
Enter fullscreen mode Exit fullscreen mode

The error looks like this for me:

ERROR in ./node_modules/react-native-swipe-gestures/index.js 121:11
Module parse failed: Unexpected token (121:11)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
Enter fullscreen mode Exit fullscreen mode

However we can try our installation on the test components the Storybook CLI added for a React.js project inside of src/stories.

so open the file .storybook/main.js and change the stories array

From

module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
...
}
Enter fullscreen mode Exit fullscreen mode

To

module.exports = {
  "stories": [
    "../src/stories/**/*.stories.mdx",
    "../src/stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
...
}
Enter fullscreen mode Exit fullscreen mode

and then run the script:

yarn start-storybook-web
Enter fullscreen mode Exit fullscreen mode

and it should compile! Behold! ๐Ÿฅณ

React.js Storybook

4.) Adding our React Native Stories to Storybook ๐Ÿš€

Close any instances and let's start adding our react native stories to our Storybook React.js setup.

Again let's modify .storybook/main.js to load our React Native written components and *.stories.js files.

From the above stories configuration

  "stories": [
    "../src/stories/**/*.stories.mdx",
    "../src/stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
Enter fullscreen mode Exit fullscreen mode

To

stories: ['../src/components/**/*.stories.[tj]s'],
Enter fullscreen mode Exit fullscreen mode

Note that I removed the .mdx files, I don't use that

Again, running the yarn start-storybook-web script will result in an error, because we have not configured the React.js Storybook installation to use React Native Web yet in a custom Webpack config.

so let's do that!

5.) Add a custom Webpack configuration to Storybook ๐Ÿš€

Storybook already comes with a Webpack configuration which we don't really want to modify, but rather inject our own stuff into. And since we already what we want to configure, as described in Step 2 of the series, where we got React Native Web working with React.js, we have ALMOST all the stuff we want to inject into the Storybook webpack configuration already prepared. (We are missing one alias soon to be described)

So where do we inject our stuff?

open .storybook/main.js and at the top of the file import our webpack configuration like this:

const custom = require('../webpack.config.js');
Enter fullscreen mode Exit fullscreen mode

and then in the module.exports = { ... } add an entry called webpackFinal like this:

const custom = require('../webpack.config')

module.exports = {
  stories: ['../src/components/**/*.stories.[tj]s'],
  webpackFinal: (config) => {
    return {
      ...config,
      resolve: { alias: { ...config.resolve.alias, ...custom.resolve.alias } },
      module: { ...config.module, rules: custom.module.rules },
    }
  },
}
Enter fullscreen mode Exit fullscreen mode

In this way we don't overwrite, or destroy the Webpack configuration that Storybook already comes with, but rather we inject our own alias rules and our own module.rules into it.

Note: yes yes I removed the addons array

Also let's not forget that we need to modify our webpack.config.js because we want atleast more things in our aliases:

all @storybook/react-native imports should resolve to @storybook/react

because in the React Native side we are always using the import from @storybook/react native and obviously that's not what we want on the web side of Storybook. First the component go through React Native Web so there is no trace left of React Native Code in them after being compiled, and then we want to run Storybook as "normal" on them.

const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')

const HTMLWebpackPluginConfig = new HTMLWebpackPlugin({
  template: path.resolve(__dirname, './public/index.html'),
  filename: 'index.html',
  inject: 'body',
})

module.exports = {
  entry: path.join(__dirname, 'index.web.js'),
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, '/build'),
  },
  resolve: {
    alias: {
      'react-native$': 'react-native-web',
      '@storybook/react-native': '@storybook/react', //<-here
    },
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules\/(?!()\/).*/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
          },
        },
      },
    ],
  },
  plugins: [HTMLWebpackPluginConfig],
  devServer: {
    historyApiFallback: true,
    contentBase: './',
    hot: true,
  },
}
Enter fullscreen mode Exit fullscreen mode

and if that is super confusing to you please read Step 2, where I try my best to explain the webpack part ๐Ÿ˜…

Let's try our yarn start-storybook-web script again and see if it runs!

yarn start-storybook-web
Enter fullscreen mode Exit fullscreen mode

๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€ BEHOOOLD! ๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€

Storybook web running react native components

Adding Styled components to webpack

Just for the ones of us that want's to use styled components when we create our react native components, add this line to your aliases in the webpack config and it should hook right in ๐Ÿ‘จโ€๐Ÿ’ป

'styled-components/native': 'styled-components',
Enter fullscreen mode Exit fullscreen mode

so aliases look like this:

    alias: {
      'react-native$': 'react-native-web',
      '@storybook/react-native': '@storybook/react',
      'styled-components/native': 'styled-components',
    },
Enter fullscreen mode Exit fullscreen mode

Fin!

Hope this was educational and a little cool!

Now you can host a static website with your React Native components, or you can actually develop them in a browser without firing up a simulator / emulator. Which is really niiiiiiice, especially if you are running on an older machine!

See the full repo here! --> Github: react-native-storybook-boilerplate

I got it hosted on Netlify, and again, the components are all written in React Native syntax!

Hosted Boilerplate

The other parts again!

Step Link
Series: The ultimate react native ui library starter repo Step 0 link
Step 1: Setting up react native with Storybook Step 1 link
Step 2: Setting up react with react native web Step 2 link

Thanks! ๐ŸŽ‰

Top comments (9)

Collapse
 
olya777 profile image
Olga Streltsova

Make sure to also spread your config.resolve

webpackFinal: (config) => {
    return {
      ...config,
      resolve: { ...config.resolve, alias: { ...config.resolve.alias, ...custom.resolve.alias } },
      module: { ...config.module, rules: custom.module.rules },
    }
  },
Enter fullscreen mode Exit fullscreen mode

This wasted about a day for me.

Collapse
 
hurrellt profile image
Tomรกs Hurrell

Hey! Great articles, they are really helpful. I cannot get storybook web to detect my stories. I tried everything I found, but they refuse to apear (they work on mobile tho).
Storybook runs and gives me this error.

Couldn't find any stories in your Storybook.

  • Please check your stories field of your main.js config.
  • Also check the browser console and terminal for error messages.

Any advice?
Thanks!

Collapse
 
ugglr profile image
Carl-W

Hi! I'm glad you are finding my articles helpful. It sounds to me like you are not pointing your web installation to the folder where you keep your stories

Collapse
 
amahajan87 profile image
Aakanksha Mahajan

This is exactly what I have been working on and this is a great resource. I have a question, do you have any points on how to make Storybook work with

  1. a11y - as soon as im adding the a11y addon, the components do not load.
  2. Styled Components - when using styled components instead of Stylesheets,
Collapse
 
ugglr profile image
Carl-W

Thank you for the response!

My experience is that the more things needed to be aliased the harder it get's to scale. When it comes to styled-components it's easy to alias in the webpack configuration because they offer packages for both sides. For the web side they have their normal package styles-components and for react-native they have styled-components/native package.

I have not personally used a11y in this case, and I'm not sure how you are using it. but I'm going to guess you will need to think about how it's used on both sides, web+native

Collapse
 
vsleichert profile image
vSleichert

I have tried this solution in our kind of old project and unfortunately for our usage it is impossible to use since react-native-web can not process components that are really native. Instead of that, you would have to mock the functionality on your own using aliases like this in webpack.config and create a mocking file, where you would mock those packages to at least simulate the functionality.

config.resolve.alias = {
    'react-native$': 'react-native-web',
    '@storybook/react-native': '@storybook/react',
    'react-native-safe-area-view': 'aliases',
    'react-native-popover-view': 'aliases/Popover-view',
    'react-native-platforms': 'aliases/Platform',
    'react-native-platform-touchable': 'aliases/PlatformTouchable',
    'react-native-vector-icons': 'aliases/react-native-vector-icons',
  };
Enter fullscreen mode Exit fullscreen mode

(If you had any idea to do it differently, please let me know).

There are tons of cons on this. You would have to keep your mocks updated with the packages, you have to style it perfectly the same...

We have found out that it would be too much effort and canceled it.
Anyway, thanks for walkthrough, it was worth a try and I believe that for some very simple apps, this can be useful.

Collapse
 
stevetro profile image
SteveTr

Thanks for your tutorial, Part 1 and 2 was really helpful.
But when i try to run "yarn storybook-web" i get a windows script error at

"node_modules\@storybook\react\bin\index.js"

Line: 1
Char: 1
Error: Invalid character
Code: 800A03F6
Source: Microsoft JScript compilation error

couldn't find a solution for that, do you have a idea?

Collapse
 
shootermv profile image
Moshe • Edited

i solved it by installing 'cross-env' (npm i cross-env)and modifing script to be "cross-env ./node_modules/@storybook/react/bin/index.js"

Collapse
 
quizzydev profile image
quizzyDev

Awesome walkthrough! Very helpful and it works!