A studio for designing and shipping shadcn-style components in Expo/React Native with Storybook-backed visual regression.
- We’re in an AI-heavy era where code volume exploded and manual review misses subtle regressions. Visual regression + deterministic stories catch the flakiness and design drift that slip past PR review.
- Frontend teams are stretched between UI polish and business logic. By separating presentational UI (
packages/ui) from product code (apps/expo), designers (or AI-assisted flows) can own the UI layer while app devs focus on data/flows. - Cross-platform (native + web) Storybook coverage is still sparse in the community; this repo offers a concrete pattern for React Native Web + Expo with Chromatic baked in.
- We want a faster design-to-prod loop: examples drive generated stories, Chromatic locks visuals, and the app shell simply consumes the shared UI.
- The repo ships with lots of concrete code (components, screens, Storybook setup, scripts) so a coding agent—or anyone new—can follow the conventions and spin up new features or variants quickly without rediscovering patterns.
- Install deps (below), then run
pnpm start:storybookto regenerate stories and open the Storybook-enabled Expo bundle. - Edit or add
*.examples.tsxnext to components inpackages/ui/src/native/**to drive Storybook; rerunpnpm start:storybook(orpnpm --filter expo-app storybook:generate:auto) to regenerate. - For visual regression, add
CHROMATIC_PROJECT_TOKENtoapps/expo/.env.localand runpnpm chromaticto upload the web Storybook build.
corepack enable # let Corepack manage the pnpm shim
corepack install # install the pinned pnpm version from package.json
pnpm install # install all workspace deps (preinstall will verify Node/pnpm versions)
pnpm dlx husky # install git hooks (pre-commit runs pnpm format:fix; pre-push runs pnpm typecheck && pnpm lint:fix)- Follow the instrcution in expo doc of:
- choose android emulator and ios simulator respectively
- choose development build
- choose to not using Expo Application Services (EAS)
- Do not run the local commands in the webpage
-
Follow this doc to setup ios
-
Execute these commands
cd apps/expo
pnpm storybook:generate:auto && pnpm storybook-generate # Generate the required files for Storybook
pnpm web # start the web dev server to confirm the toolchain
npx expo install expo-dev-client # install native dev client deps for iOS/Android
pnpm android # build and launch the Android dev client in the emulator
pnpm ios # build and launch the iOS dev client in the simulatorpnpm dev # run turbo dev across packages in parallel
pnpm ios # run expo-app iOS dev build from the repo root
pnpm android # run expo-app Android dev build from the repo root
pnpm web # run expo-app web dev server from the repo root
pnpm --filter expo-app start # start Expo bundler and choose ios/android/web in the prompt
pnpm format:fix # auto-format sources via Prettier across packages
pnpm lint:fix # auto-fix lint issues across packages
pnpm lint && pnpm typecheck # run eslint + typescript checks
pnpm test # run Jest (set WATCHMAN_DISABLE=1 if watchman is unavailable)- Native/on-device (Expo bundler, also works for web if you choose the web target):
pnpm start:storybook, then open the/storybookroute in the app (Expo Router). Usepnpm web:pureto run a pure app web bundle, orpnpm web:storybookto include Storybook in the web bundle explicitly. - Web (browser UI via Vite/React Native Web):
pnpm storybookfrom the repo root (opens on port 6006).
- Pure app shell: run
pnpm web:pure(orpnpm --filter expo-app startwithout settingEXPO_PUBLIC_STORYBOOK_ENABLED) to load only the app screens. - Storybook-enabled bundle: run
pnpm start:storybook(native or web target) orpnpm web:storybookfor web-only. Both setEXPO_PUBLIC_STORYBOOK_ENABLED=truebefore starting the Expo bundler. - In the running app, tap the
Open Storybook (/storybook)button on the home screen Navigation card or in the Tasks screen header. You can also type/storybookin the app URL once the Storybook bundle is enabled. - Switch modes by restarting the bundler with or without
EXPO_PUBLIC_STORYBOOK_ENABLED; the Storybook button hides itself when the bundle is pure app-only, so you can confirm the mode visually.
-
Cross-platform view (web + iOS + Android) with Storybook toggle visible:
-
Home shell with jump buttons (tap
Open Storybook (/storybook)to switch): -
Storybook running at
/storybookafter enablingEXPO_PUBLIC_STORYBOOK_ENABLED=true: -
Tasks screen with the same Storybook jump in the header:
- Build presentational UI in
packages/uionly (no router/hooks/network/state there). Keep business logic, data, navigation, and side-effects inapps/exporoute wrappers. - For each component/screen, add a
*.examples.tsxnext to it exportingstoryMeta+storyExamples(deterministic data, no side-effects). - Run
pnpm start:storybook(root). It regenerates stories and Storybook requires files automatically, then starts Expo with Storybook enabled. Inspect/storybookon device/web for quick visual checks. - Husky hooks already help:
- pre-commit: runs
pnpm --filter expo-app storybook:generate:auto+pnpm format:fix - pre-push: runs
pnpm typecheck+pnpm lint:fix
- pre-commit: runs
- Manual: run
pnpm lint && pnpm typecheckif you skip hooks or want to check early;pnpm format:fixcleans up formatting.
- Separation of concerns:
packages/ui= presentational React Native UI only;apps/expo/app/*= Expo Router shells that own state, data fetching, navigation, analytics, and pass props down. - UI-first: prioritize stable, deterministic examples for visual regression. Avoid backend SDKs or product-specific logic in the UI package.
- Storybook auto-gen: examples drive generated stories under
apps/expo/.rnstorybook/stories/auto/**. Do not edit generated files; change examples and rerunpnpm start:storybook. - Chromatic: CI publishes the web Storybook to Chromatic for visual diffs; keep examples deterministic to make snapshots reliable.
- We use Chromatic to publish the web Storybook. CI runs
pnpm chromatic(which calls theapps/expoChromatic script) on in-repo PRs. - Setup for contributors:
- Local: put
CHROMATIC_PROJECT_TOKEN=...inapps/expo/.env.local(gitignored). Thepnpm chromaticscript auto-loads.env.local/.envand passes the token through. - CI: store the same token in your CI secrets as
CHROMATIC_PROJECT_TOKEN.
- Local: put
- Run locally from repo root:
pnpm chromatic. This builds the React Native Web Storybook and uploads snapshots to Chromatic’s CDN using the token from.env.local/.env.



