Setting up Storybook for React Native/TypeScript (server, loader, iOS, Android)
Tags: reactnative, storybook
Published: 2021-01-10 12:59:34

---
title: Setting up Storybook for React Native/TypeScript (server, loader, iOS, Android)
published: true
description: 
tags: reactnative, storybook
//cover_image: https://direct_url_to_image.jpg
---

I recently set up Storybook for a React Native/TypeScript project. It wasn't so different from using Storybook for web dev, but there were a few catches. I found their [official tutorial](https://www.learnstorybook.com/intro-to-storybook/react-native/en/get-started/) to be outdated sometimes, and their [Github README](https://github.com/storybookjs/react-native) was up-to-date but didn't have all the information I wanted.

This is a step-by-step walkthrough of how to set up Storybook for React Native, complete with the the web UI ([`@storybook/react-native-server`](https://www.npmjs.com/package/@storybook/react-native-server)), dynamic story loading ([`react-native-storybook-loader`](https://github.com/elderfo/react-native-storybook-loader)), and TypeScript.

Finished repo:
{% github risafj/StorybookExampleReactNativeTS %}

## Prerequisites

Since you're reading this post, I'm going to assume you already have a project set up using the React Native CLI.

_NOTE: I can't guarantee that the same steps will work for Expo projects._

## Step 1: Install Storybook

Run the command below from your project root. This will install the necessary packages and add boilerplate code for you.

```
npx -p @storybook/cli sb init --type react_native
```

__When it prompts you whether to install `@storybook/react-native-server`, hit yes.__
Basically, the Storybook Server package lets you use the web interface for switching between components and manipulating knobs, instead of having to do everything from your emulator (it's the same UI as Storybook for other frameworks).

This is what it will look like with the web interface:

![storybook-server-gif](https://dev-to-uploads.s3.amazonaws.com/i/6m03cu04fbrd15gi0ywc.gif)


If you're developing for iOS, you should also run a `pod install` from the `ios/` directory.

## Step 2: Conditionally render Storybook

Now, let's add the enviroment variable `LOAD_STORYBOOK`, and use this flag to determine whether we should load your actual app or Storybook.

_NOTE: This is one of the ways recommended in Storybook's [README](https://github.com/storybookjs/react-native#other-ways-to-render-storybook)._

### Add `LOAD_STORYBOOK` flag

There are a few ways to use environment variables in React Native, but I used [`react-native-config`](https://github.com/luggit/react-native-config).
Setup is very simple:

1. `yarn add react-native-config`, then `pod install`
2. If you're developing for Android, add an import line as described [here](https://github.com/luggit/react-native-config#extra-step-for-android)
3. In your project root, create an `.env` file
4. Any environment variables you add to `.env` can be accessed as `Config.YOUR_ENVIRONMENT_VARIABLE`

Now, add the environment variable `LOAD_STORYBOOK=true` to `.env`.

### Render Storybook if `LOAD_STORYBOOK=true`
In `App.tsx`, change your code like below so that Storybook is rendered conditionally.

```tsx
import StorybookUI from './storybook'
import Config from 'react-native-config'

const App = () => {
  return (
    // Your actual app
  )
}

export default Config.LOAD_STORYBOOK === 'true' ? StorybookUI : App
```

## Step 3: Boot Storybook

There are a some other things to configure, but at this point, you should be able to boot Storybook and make sure it works.

First, boot Storybook Server (the web interface) with this command:

```
yarn storybook
```

When it's done booting, a browser window should open that looks like this (if not, you can just access `localhost:7007`):

![empty-storybook-server](https://dev-to-uploads.s3.amazonaws.com/i/pxbhbnu0x5il4qbqwoyg.png)

At this point, the sidebar menu should show a loading animation.

Next, run `yarn ios` or `yarn android`. Since we set `LOAD_STORYBOOK=true`, this runs Storybook instead of your actual app.

Once it boots, the Storybook Server's sidebar should be populated with the stories you have, allowing you to navigate via the web UI (like the GIF from step 1).

### Steps to take if Storybook Server doesn't work with the Android emulator

For Android, you may find that the sidebar doesn't get populated even when Storybook has booted in your emulator. There were some Github issues (like [this one](https://github.com/storybookjs/storybook/issues/6674)) discussing this phenomenon. The advice I found online was to run `adb reverse tcp:7007 tcp:7007`, but ultimately, what solved it for me was to specify the host parameter to be the Android localhost.

```js
// storybook/index.js

const StorybookUIRoot = getStorybookUI({
  // Add the line below
  host: Platform.OS === 'android' ? '10.0.2.2' : '0.0.0.0'
});
```

## Step 4: Take care of asyncStorage warning

You'll probably see a warning in the metro server logs that looks like this:

`WARN Starting Storybook v5.3.0, we require to manually pass an asyncStorage prop. Pass null to disable or use one from @react-native-community or react-native itself.`

According to the [docs](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#react-native-async-storage):
> The benefit of using Async Storage is so that when users refresh the app, Storybook can open their last visited story.

Configuring this is simple; just pass it in as below.

_NOTE: If you don't have the `@react-native-community/async-storage` package, you'll have to install it first._

```js
// storybook/index.js

const StorybookUIRoot = getStorybookUI({
  host: Platform.OS === 'android' ? '10.0.2.2' : '0.0.0.0',
  // Add the line below
  asyncStorage: require('@react-native-community/async-storage').default
});
```

## Step 5: Add Storybook Loader

React Native Storybook Loader is technically not necessary for using Storybook, but it's a convenient package that frees you from the task of having to write an import statement for every story file. Storybook Loader executes a script when you boot up Storybook, which auto-generates a file with the necessary import statemnents.

Their official README's quick start section covers everything you need, so I will link it here: https://github.com/elderfo/react-native-storybook-loader#quick-start

## Step 6: Write your own stories (in TypeScript!)

Now, all that's left is to add your own stories (and remove unnecessary boilerplate code). You don't have to add any packages or configuration for writing your stories in TypeScript; just use the `.stories.tsx` extension.
Here is an example:

```tsx
// src/components/atoms/CustomButton.stories.tsx

import { storiesOf } from '@storybook/react-native'
import { CenterView } from '../../../storybook/stories/CenterView'
import React from 'react'
import { CustomButton } from './CustomButton'

storiesOf('Atoms/CustomButton', module)
  .addDecorator((getStory) => <CenterView>{ getStory() }</CenterView>)
  .add('confirm', () => (
    <CustomButton text="Confirm" colorModifier="confirm" />
  ))
```

### Caveat
If you plan to reuse `CenterView`, the boilerplate component that places your story code at the center of the emulator screen, you need to rewrite it in TypeScript or you will encounter some type errors. This is fairly straightforward - just rewrite it like so (the exact changes can be seen in [this commit](https://github.com/risafj/StorybookExampleReactNativeTS/commit/f81ed67bb23b9a522429eeb0d213dd08754ecb1b)):

```tsx
// storybook/stories/CenterView/index.tsx

import React from 'react'
import { StyleSheet, View } from 'react-native'

interface Props {
  children: any
}

export const CenterView = (props: Props) => {
  return (
    <View style={ styles.main }>
      { props.children }
    </View>
  )
}

const styles = StyleSheet.create({
  main: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  }
})
```

Thank you for reading!

-----
Credits:
Many thanks to @rob117, who helped with the solution to the compatibility issue between the Storybook Server and Android emulator.