Logo

dev-resources.site

for different kinds of informations.

Run Storybook with NX Expo and React Native Paper

Published at
12/17/2024
Categories
reactnative
storybook
nx
expo
Author
typescriptteatime
Categories
4 categories in total
reactnative
open
storybook
open
nx
open
expo
open
Author
17 person written this
typescriptteatime
open
Run Storybook with NX Expo and React Native Paper

tl;dr: Add vite-plugin-react-native-web to your storybook config in {projectRoot}/.storybook/main.ts and you should be good to go. Scroll to end for the full config.

After several days of finally getting this running, while stumbling onto this NX issue several times, I thought I might as well share my solution, and an approach of how to debug if things go haywire (which they did - a lot).

I am assuming you have a working NX workspace with an Expo project and React Native Paper installed.

Outline

  • Add Storybook
  • Make DOM elements in Storybook work
  • Make React Native elements in Storybook work
  • Make React Native Paper elements in Storybook work

Add Storybook

Tested under version

@nx/expo@20.1.4
yarn@1.22.19
node@22.11.0
ubuntu 22.04
Enter fullscreen mode Exit fullscreen mode

Let's get started.

yarn nx add @nx/storybook
yarn nx g @nx/react:storybook-configuration PROJECT_NAME
# I answered all questions with the default selection
Enter fullscreen mode Exit fullscreen mode

In my case, this installed

    "@nx/storybook": "20.1.4",
    "@nx/vite": "20.1.4",
    "@nx/web": "20.1.4",
    "@storybook/addon-essentials": "^8.2.8",
    "@storybook/addon-interactions": "^8.2.8",
    "@storybook/core-server": "^8.2.8",
    "@storybook/jest": "^0.2.3",
    "@storybook/react-vite": "^8.2.8",
    "@storybook/test-runner": "^0.13.0",
    "@storybook/testing-library": "^0.2.2",
    "@vitejs/plugin-react": "^4.2.0",
    "storybook": "^8.2.8",
    "vite": "^5.0.0"
Enter fullscreen mode Exit fullscreen mode

Depending on your components it should generate at least one *.stories.tsx file, in my case App.stories.tsx.

If you start this and it renders perfectly for you. Congratz. You don't need to read further. If you're anything like me, then it doesn't render and here's how I debugged it.

Make DOM elements in Storybook work

I check that at least the most basic setup works by editing App.stories.tsx and replace the file contents with the most limited I can think of

import { expect } from "@storybook/jest";
import type { Meta, StoryObj } from "@storybook/react";
import { within } from "@storybook/testing-library";
import { App } from "./App";

const XComp = () => <div>Hi</div>;

const meta: Meta<typeof App> = {
  component: XComp,
  title: "App",
};
export default meta;
type Story = StoryObj<typeof App>;

export const Primary = {
  args: {},
};
Enter fullscreen mode Exit fullscreen mode

Running yarn nx storybook PROJECT_NAME should open the browser and you should see the text 'Hi'.

Image

Make React Native elements in Storybook work

Next up is including something from React Native.

So I change the component definition in App.stories.tsx to

import { Text } from "react-native";

const MyComponent = () => <Text>Hi</Text>;
Enter fullscreen mode Exit fullscreen mode

This already starts to fail.
The webapp displays an error Failed to fetch dynamically imported module: http://localhost:35021/src/app/App.stories.tsx while the console shows

✘ [ERROR] Unexpected "typeof"

    ../../node_modules/react-native/index.js:14:7:
      14 │ import typeof ActionSheetIOS from './Libraries/ActionSheetIOS/ActionSheetIOS';
         ╵        ~~~~~~

9:15:52 PM [vite] error while updating dependencies:
Error: Build failed with 1 error:
../../node_modules/react-native/index.js:14:7: ERROR: Unexpected "typeof"
Enter fullscreen mode Exit fullscreen mode

This is still 'easy' to fix. Because we're rendering for the web, we should use react-native-web instead of react-native. Obviously, I don't want to hardcode the import to react-native-web as that would not be beneficial for my component library for expo mobile dev. We can however leverage resolve.aliases to import from react-native-web whenever our code says import ... from 'react-native'. (I assume this is what happens under the hood in Expo depending on your target platform. Maybe you can teach me or link to some references in the comments. I'd appreciate it!).

Anyway, to resolve the correct import, we go into {projectRoot}/.storybook/main.ts and replace the config by

const config: StorybookConfig = {
  stories: ["../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))"],
  addons: ["@storybook/addon-essentials", "@storybook/addon-interactions"],
  framework: {
    name: "@storybook/react-vite",
    options: {},
  },

  viteFinal: async (config) =>
    mergeConfig(config, {
      plugins: [react(), nxViteTsPaths()],
      resolve: {
        alias: {
          "react-native": "react-native-web",
        },
      },
    }),
};
Enter fullscreen mode Exit fullscreen mode

Testing again by rerunning yarn nx storybook PROJECT_NAME should now render the text 'Hi' again. Nice!

So now we have storybook, we can theoretically use regular DOM elements, but more importantly we can use React Native components. A good point to stage and commit our changes. Then it's up to the next level.

Make React Native Paper elements in Storybook work

In my case, I am building on top of React Native Paper, so the natural next step is to change MyComponent to include

import { PaperProvider, Text } from "react-native-paper";

const MyComponent = () => (
  <PaperProvider>
    <Text>Hi</Text>
  </PaperProvider>
);
Enter fullscreen mode Exit fullscreen mode

This again will fail to render, and the console shows

✘ [ERROR] Expected "from" but found "{"

    ../../node_modules/react-native-vector-icons/lib/NativeRNVectorIcons.js:3:12:
      3 │ import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
        │             ^
        ╵             from

✘ [ERROR] The JSX syntax extension is not currently enabled

    ../../node_modules/react-native-vector-icons/lib/icon-button.js:116:8:
      116 │         <TouchableHighlight
          ╵         ^

  The esbuild loader for this file is currently set to "js" but it must be set to "jsx" to be able
  to parse JSX syntax. You can use "loader: { '.js': 'jsx' }" to do that.
Enter fullscreen mode Exit fullscreen mode

The problem is that these files are not plain javascript files but include Flow types, and also JSX files.

While the error tells us to customize our loader to treat every js file as jsx, that won't solve the problem with the Flow types in TurboModule. At this point we could probably dive deeper to strip the flow types. But what if we simply solve both problems at once, and the problem of resolving the right import from earlier, too?

Enter vite-plugin-react-native-web!

We install it by running

yarn add -D vite-plugin-react-native-web
Enter fullscreen mode Exit fullscreen mode

and then we go back to {projectRoot}/.storybook/main.ts and replace the config by

import type { StorybookConfig } from "@storybook/react-vite";

import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";
import react from "@vitejs/plugin-react";
import { mergeConfig } from "vite";
import reactNativeWeb from "vite-plugin-react-native-web";

const config: StorybookConfig = {
  stories: ["../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))"],
  addons: ["@storybook/addon-essentials", "@storybook/addon-interactions"],
  framework: {
    name: "@storybook/react-vite",
    options: {},
  },

  viteFinal: async (config) =>
    mergeConfig(config, {
      plugins: [react(), nxViteTsPaths(), reactNativeWeb()],
    }),
};

export default config;
Enter fullscreen mode Exit fullscreen mode

Notice that we also got rid of the resolve.alias part. This is because vite-plugin-react-native-web will take care of that for us.

Now we can rerun yarn nx storybook PROJECT_NAME and the text 'Hi' should render again. (If you don't get an error but a blank canvas inside storybook, then the theme is probably off. Try switching to dark/light theme in the storybook UI and the text should become visible.)

And that's it! We now have a working storybook setup with React Native Paper components.

Kudos go to the maintainers of vite-plugin-react-native-web for making this possible.

expo Article's
30 articles in total
Favicon
Read Text Asset File in Expo
Favicon
Run Storybook with NX Expo and React Native Paper
Favicon
Explorando Notificações Push no React Native com Expo e OneSignal!
Favicon
Starting React Native Project in 2025
Favicon
Guia Completo: Como Integrar WatermelonDB no React Native 0.76 com Expo 52 e TypeScript
Favicon
How to create authenticated routes with the new Expo SDK 51 using Expo Router
Favicon
React Native Expo with NativeWind v4 and Typescript
Favicon
Translate & Guess: Build a Flag Quiz with Expo and Tolgee
Favicon
How i implemented my server login screen for Mastodon
Favicon
How to Change iOS Push Notifications Permission Dialog with Expo
Favicon
Exploring React Native Navigation with Expo: A Complete Guide
Favicon
How to Render OpenStreetMap in an Expo React Native Web App Using React Leaflet
Favicon
EAS build reads your .gitignore file
Favicon
#2 - Expo apk keeps on crashing after build
Favicon
Dear expo, who are you?
Favicon
npm i openai-react-native
Favicon
Expo51 Jotai Template + flashlist + tamagui
Favicon
Scroll-Responsive Animated Header Bar with Expo Router
Favicon
Expo vs. React Native: Pros, Cons, and Key Differences
Favicon
To Obfuscate or Not Obfuscate (React Native)
Favicon
How to disable keyboard suggestions for Android in React Native
Favicon
Expo51 Jotai Template, ready to use
Favicon
Let's get started with React Native + Expo
Favicon
Generar APK con EAS ⚛️ React Native
Favicon
How to publish your React Native app to Expo Store 2024
Favicon
Creating a WebView app in React Native using Expo
Favicon
Embracing Expo: The New Standard for Creating React Native Apps
Favicon
Automate Your Expo Builds with EAS Using GitHub Actions: A Step-by-Step Guide (Android)
Favicon
How to Generate APK Using React Native Expo
Favicon
Creating Chat Bubbles with curls in React Native (svg)

Featured ones: