Sharing and Deploying Storybook
Storybook is excellent for gathering feedback and ensuring components work correctly across platforms. This guide shows how to share your Storybook with team members.
iOS Distribution via TestFlight
TestFlight enables sharing with up to 10,000 internal testers without rebuilding for each new team member.
Why TestFlight?
Traditional ad hoc provisioning requires:
- Rebuilding for each new team member
- Devices in developer mode
- Manual UDID registration
TestFlight eliminates these requirements by using App Store distribution.
Create a separate app in App Store Connect specifically for Storybook preview builds. This app won’t ship to the App Store.
Setup Guide
Create a new Expo app (if needed)
npx create-expo-app --template expo-template-storybook@next AwesomeStorybook
cd AwesomeStorybook
Configure EAS
Install EAS CLI and initialize the project:npm install -g eas-cli
eas login
eas init
eas build:configure -p all
eas update:configure
Configure eas.json for Storybook builds
Add a storybook build profile:{
"build": {
"storybook": {
"distribution": "store",
"channel": "storybook",
"autoIncrement": true,
"ios": {
"simulator": false
},
"env": {
"EXPO_PUBLIC_ENVIRONMENT": "storybook",
"EXPO_PUBLIC_STORYBOOK_ENABLED": "true"
}
}
},
"submit": {
"storybook": {
"ios": {
"bundleIdentifier": "com.example.myapp-storybook",
"appName": "My App Storybook"
}
}
}
}
Replace com.example.myapp-storybook with your own bundle identifier. Use a different identifier than your production app.
Create app.config.ts
Dynamically set bundle identifier based on environment:// app.config.ts
import { type ExpoConfig, ConfigContext } from 'expo/config';
function getBundleIdentifier() {
const isStorybook = process.env.EXPO_PUBLIC_ENVIRONMENT === 'storybook';
if (isStorybook) {
return 'com.example.myapp.storybook';
}
return 'com.example.myapp';
}
function config({ config }: ConfigContext): Partial<ExpoConfig> {
return {
// Config from app.json
...config,
ios: {
...config.ios,
bundleIdentifier: getBundleIdentifier(),
infoPlist: {
...config.ios?.infoPlist,
ITSAppUsesNonExemptEncryption: false,
},
},
};
}
export default config;
Build and submit to TestFlight
eas build -p ios --submit --profile storybook
You’ll receive an email when the app is ready for TestFlight distribution.Invite testers
In App Store Connect:
- Navigate to TestFlight
- Add internal or external testers
- Testers receive email invitations
- They can install via the TestFlight app
Updating Storybook via OTA
After initial TestFlight distribution, push updates without rebuilding:
# Make changes to your stories
eas update --branch storybook --message "Updated Button component"
Users get updates on next app launch.
Android Distribution
Internal Testing (Play Store)
Update eas.json
Same configuration as iOS, but ensure Android package name is set:{
"submit": {
"storybook": {
"android": {
"applicationId": "com.example.myapp.storybook"
}
}
}
}
Update app.config.ts
Add Android package configuration:function config({ config }: ConfigContext): Partial<ExpoConfig> {
return {
...config,
android: {
...config.android,
package: getBundleIdentifier(),
},
};
}
Build and submit
eas build -p android --submit --profile storybook
APK Distribution (Internal Sharing)
For teams not using Play Store:
{
"build": {
"storybook-internal": {
"extends": "storybook",
"distribution": "internal",
"android": { "buildType": "apk" }
}
}
}
Build the APK:
eas build -p android --profile storybook-internal
Share the download link from the EAS build page:
Web Deployment
Storybook UI is compatible with React Native Web. Deploy to the web for easy access:
Build for web
EXPO_PUBLIC_ENVIRONMENT=storybook npx expo export --platform web
This generates static files in dist/.Deploy with EAS
Expo provides a hosted URL for your Storybook. Access your Storybook
Visit the URL provided by EAS (e.g., https://yourproject--hash.expo.app/).
Alternative Web Hosting
Netlify
Vercel
GitHub Pages
# Build
EXPO_PUBLIC_ENVIRONMENT=storybook npx expo export --platform web
# Deploy
netlify deploy --dir=dist --prod
# Build
EXPO_PUBLIC_ENVIRONMENT=storybook npx expo export --platform web
# Deploy
vercel --prod
# Build with base path
EXPO_PUBLIC_ENVIRONMENT=storybook npx expo export --platform web
# Deploy to gh-pages branch
npx gh-pages -d dist
CI/CD Integration
Automate builds and deployments:
GitHub Actions Example
name: Deploy Storybook
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm install
- name: Build Storybook for web
run: EXPO_PUBLIC_ENVIRONMENT=storybook npx expo export --platform web
- name: Deploy to EAS
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
run: eas deploy
Best Practices
Separate Bundle Identifiers: Always use different bundle identifiers/package names for Storybook preview builds vs. production apps.
Version Control: Use EAS channels (storybook, production) to manage different build variants.
OTA Updates: Leverage EAS Updates for quick iterations without rebuilding native binaries.
Environment Variables: Use EXPO_PUBLIC_STORYBOOK_ENABLED to toggle Storybook on/off. Combine with Metro’s enabled option to exclude Storybook from production bundles.
Troubleshooting
TestFlight Build Missing
- Verify bundle identifier is unique and registered in App Store Connect
- Check that
ITSAppUsesNonExemptEncryption: false is set in infoPlist
- Review build logs in EAS dashboard
Web Build Fails
- Ensure all dependencies are React Native Web compatible
- Check for native-only code in stories (use platform-specific files if needed)
- Verify Metro config doesn’t interfere with web builds
OTA Updates Not Appearing
- Confirm app and update are on the same channel
- Check update message in EAS dashboard
- Force close and reopen the app
- Verify
runtimeVersion policy in eas.json