How to Setup React Native Web with Storybook

TLDR

Create a file named webpack.config.js within the .storybook folder with an alias for React Native Web.

jsx
1.module.exports = {
2. ...
3. resolve: {
4. alias: {
5. "react-native$": "react-native-web"
6. }
7. }
8.};

Intro

Storybook is an excellent component development environment for React and other frameworks. However, if you’re using React Native Web or would like to create some RNW components with Storybook you’ll need to add some more configuration. If you already have a React or React Native Web app setup then you might want to skip to the Render React Native Web with Storybook section of this article because the first section will start from scratch.

Basic React Setup

First, initialize a package.json.

jsx
1.npm init

Then, install React, Babel, Webpack, and the usual loaders for running a typical React app.

shell
1.npm install react react-dom babel-loader @babel/core @babel/preset-env @babel/preset-react html-loader html-webpack-plugin webpack webpack-dev-server webpack-cli

Create Webpack config

Create webpack.config.js at the root of the project with the following basic react configuration.

js
1.const HtmlWebPackPlugin = require("html-webpack-plugin");
2.
3.module.exports = {
4. module: {
5. rules: [
6. {
7. test: /\.(js|jsx)$/,
8. exclude: /node_modules\/(?!()\/).*/,
9. use: {
10. loader: "babel-loader",
11. options: {
12. presets: ["@babel/preset-env", "@babel/preset-react"],
13. },
14. },
15. },
16. {
17. test: /\.html$/,
18. use: [
19. {
20. loader: "html-loader",
21. },
22. ],
23. },
24. ],
25. },
26. plugins: [
27. new HtmlWebPackPlugin({
28. template: "./public/index.html",
29. filename: "./index.html",
30. }),
31. ],
32. devServer: {
33. historyApiFallback: true,
34. contentBase: "./",
35. hot: true,
36. },
37.};

Public Folder and HTML

Create a public folder at the project root. Inside that folder, create a index.html with the following HTML:

html
1.<!DOCTYPE html>
2.<html>
3. <head>
4. <title>React Native Web Storybook</title>
5. </head>
6. <body>
7. <div id="app"></div>
8. </body>
9.</html>

App Entry and Component

Create a folder src at the project root. Inside that folder, create index.js with the following React setup code:

jsx
1.import React from "react";
2.import ReactDOM from "react-dom";
3.
4.import App from "./App";
5.
6.ReactDOM.render(<App />, document.getElementById("app"));

Next, create App.js inside the src folder with the following “Hello World” code

jsx
1.import React from "react";
2.
3.export default class App extends React.Component {
4. render() {
5. return <div>Hello world</div>;
6. }
7.}

Running Hello World

Add the following two scripts to the scripts object in package.json for running and building the app with Webpack.

jsx
1."scripts": {
2. "build": "webpack --mode production",
3. "start": "webpack-dev-server --config ./webpack.config.js --mode development",
4.},

Finally, run npm start in your terminal and hello world should be shown at http://localhost:8080/.

React Setup

Setup Storybook with React

First, install Storybook.

shell
1.npm i @storybook/react

Create a script, storybook, for starting the storybook dev server.

jsx
1."scripts": {
2. "build": "webpack --mode production",
3. "start": "webpack-dev-server --config ./webpack.config.js --mode development",
4. "storybook": "start-storybook"
5.},

Create a folder, .storybook, at the project root. Create another project file, main.js within that folder and add the following:

jsx
1.module.exports = {
2. stories: ["../src/**/*.stories.[tj]s"],
3.};

Create index.stories.js within src to test that Storybook is configured correctly.

jsx
1.import React from "react";
2.
3.export default { title: "Hello World" };
4.
5.export const withText = () => <div>Hello World</div>;

Finally, run npm run storybook, and the storybook server should start running on the port indicated in your terminal. In my case, the server is running on http://localhost:57823/.

Storybook setup

Setup React Native Web

First, install React Native Web.

shell
1.npm i react-native-web

Go to webpack.config.js at the project root and add an alias for react-native to react-native-web so Webpack will pull components from react-native-web instead of React Native. Add the following below plugins and above devServer:

js
1....
2. resolve: {
3. alias: {
4. "react-native$": "react-native-web",
5. },
6. },
7. ...

This is the only config necessary to use React Native Web components given our current simple setup. Let’s test this configuration by adding a React Native component to src/App.js.

jsx
1.import React from "react";
2.import { Text } from "react-native";
3.
4.export default class App extends React.Component {
5. render() {
6. return <Text>Hello world</Text>;
7. }
8.}

Run the app server again with npm start and you should see “Hello World” just as before, except this time the syntax matches React Native. The main app is now set up to render React Native components.

React native web app setup

Render React Native Web with Storybook

Assuming you have React, React Native Web, and Storybook set up properly, the only step left is to add an alias for the Storybook Webpack server to look for react-native imports within react-native-web.

Create a file named webpack.config.js within the .storybook folder that resolves an alias for React Native Web as shown below.

jsx
1.module.exports = {
2. ...
3. resolve: {
4. alias: {
5. "react-native$": "react-native-web"
6. }
7. }
8.};

Now, open src/index.stories.js and create a component with React Native.

jsx
1.import { Text } from "react-native";
2....
3.
4.export const reactNative = () => <Text>React Native Web is Awesome!</Text>;

Run Storybook again with npm run storybook.

React native web app storybook

We can use any of the API’s found on React Native Web’s README. For example,

jsx
1.import { ActivityIndicator } from "react-native";
2....
3.
4.export const reactNativeActivity = () => <ActivityIndicator />;

React native web app activity

Conclusion

Getting React Native Web working with Storybook can be extremely simple, but it can also be significantly more complicated depending on the third-party libraries your app is utilizing.