Migration Plan: Big Bang Approach
Strategy Overview
The Big Bang approach rewrites the @digiwedge/react-native-paper wrapper library to use Gluestack UI v3 internals while maintaining backward-compatible exports where feasible. All mobile apps are migrated simultaneously, eliminating the need for dual-library support.
Note:
@digiwedge/react-native-paperhas been removed from the workspace; legacy screens/hooks now live in@digiwedge/mobile-auth. References below capture the migration record.
┌──────────────────────────────────────────────────────────────────┐
│ BIG BANG MIGRATION │
│ │
│ Week 1-2 Week 3-4 Week 5-6 Week 7-8 Week 9-10 │
│ ──────── ──────── ──────── ──────── ────────── │
│ Foundation Forms Overlays Business Integration │
│ & Setup & Input & Modals Logic & QA │
│ │
│ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ │
└──────────────────────────────────────────────────────────────────┘
Pre-Migration Checklist
Environment Setup
- Expo SDK 54 aligned across all apps
- React Native 0.81.5 baseline
- Node 22 LTS
- pnpm workspace configured
Audit Tasks
- Complete grep audit for all react-native-paper imports
- Document all custom theme tokens in use
- List all third-party packages depending on Paper
- Screenshot all major screens for visual comparison
Team Preparation
- NativeWind/Tailwind training session
- Gluestack v3 component API walkthrough
- Compound component pattern explanation
- Code review guidelines for migration PRs
Phase 1: Foundation & Setup (Week 1-2)
Goals
- Initialize Gluestack UI v3 in monorepo
- Configure NativeWind across all mobile apps
- Create new wrapper library scaffold
- Port core primitives
Tasks
1.1 Initialize Gluestack UI
# Use the existing libs/ui/gluestack-ui scaffold.
# Only run the CLI in libs/ui/gluestack-ui when adding new components.
cd libs/ui/gluestack-ui
pnpm dlx gluestack-ui add box text icon divider spinner
1.2 Create @digiwedge/gluestack-ui Library
libs/ui/gluestack-ui/
├── src/
│ ├── index.ts # Main exports
│ ├── components/
│ │ ├── primitives/ # Box, Text, Icon, Divider, Spinner
│ │ ├── forms/ # Button, Input, Switch, etc.
│ │ ├── overlays/ # Modal, Menu, Toast, etc.
│ │ └── business/ # Auth, Profile, Booking modals
│ ├── hooks/ # Re-export existing hooks unchanged
│ ├── stores/ # Re-export existing stores unchanged
│ ├── types/ # Re-export existing types unchanged
│ └── theme/
│ ├── tokens.ts # Design tokens
│ ├── tailwind.config.js # Tailwind configuration
│ └── Provider.tsx # GluestackUIProvider wrapper
├── package.json
└── tsconfig.json
1.3 Configure NativeWind in Apps
Each app needs:
// metro.config.js
const { withNxMetro } = require('@nx/expo');
const { getDefaultConfig } = require('@expo/metro-config');
const { mergeConfig } = require('metro-config');
const { withNativeWind } = require('nativewind/metro');
const defaultConfig = getDefaultConfig(__dirname);
const baseConfig = withNxMetro(mergeConfig(defaultConfig, {}), {
watchFolders: [],
extensions: [],
});
module.exports = withNativeWind(baseConfig, { input: './global.css' });
// .babelrc.*
module.exports = function (api) {
api.cache(true);
const isTest =
process.env.BABEL_ENV === 'test' || process.env.NODE_ENV === 'test';
return {
presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }]],
plugins: isTest ? [] : ['nativewind/babel'],
};
};
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'../../libs/ui/gluestack-ui/src/**/*.{js,jsx,ts,tsx}',
],
presets: [require('nativewind/preset')],
theme: {
extend: {
colors: {
primary: { /* token values */ },
secondary: { /* token values */ },
},
},
},
};
1.4 Port Primitives
| Paper Component | Gluestack Component | Migration Notes |
|---|---|---|
Text | Text | Direct mapping, add className support |
Surface | Box | Replace with Box, use bg-* classes |
Divider | Divider | Direct mapping |
ActivityIndicator | Spinner | Rename import |
Icon | Icon | Configure with lucide-react-native |
Deliverables
-
@digiwedge/gluestack-uilibrary created - NativeWind configured in all 4 apps
- Primitives (Box, Text, Icon, Divider, Spinner) working
- Apps build successfully with new provider (QA pending)
Phase 2: Form Components (Week 3-4)
Goals
- Migrate all form-related components
- Implement compound component wrappers
- Update form validation patterns
Tasks
2.1 Button Migration
Before (Paper):
<Button mode="contained" icon="plus" onPress={handlePress}>
Add Item
</Button>
After (Gluestack wrapper):
// @digiwedge/gluestack-ui/components/forms/Button.tsx
export const Button = ({
mode = 'contained',
icon,
children,
...props
}) => {
const variant = mode === 'contained' ? 'solid'
: mode === 'outlined' ? 'outline'
: 'link';
return (
<GlueButton variant={variant} {...props}>
{icon && <ButtonIcon as={getIcon(icon)} />}
<ButtonText>{children}</ButtonText>
</GlueButton>
);
};
2.2 TextInput → Input Migration
Before (Paper):
<TextInput
label="Email"
value={email}
onChangeText={setEmail}
error={!!errors.email}
mode="outlined"
/>
<HelperText type="error" visible={!!errors.email}>
{errors.email}
</HelperText>
After (Gluestack wrapper):
// @digiwedge/gluestack-ui/components/forms/TextInput.tsx
export const TextInput = ({
label,
error,
helperText,
mode = 'outlined',
...props
}) => {
const variant = mode === 'outlined' ? 'outline' : 'underlined';
return (
<FormControl isInvalid={!!error}>
{label && <FormControlLabel><FormControlLabelText>{label}</FormControlLabelText></FormControlLabel>}
<Input variant={variant}>
<InputField {...props} />
</Input>
{(error || helperText) && (
<FormControlHelper>
<FormControlHelperText className={error ? 'text-error-500' : ''}>
{error || helperText}
</FormControlHelperText>
</FormControlHelper>
)}
</FormControl>
);
};
2.3 IconButton Migration
Before (Paper):
<IconButton icon="menu" size={24} onPress={openMenu} />
After (Gluestack wrapper):
// @digiwedge/gluestack-ui/components/forms/IconButton.tsx
export const IconButton = ({ icon, size = 24, ...props }) => {
return (
<GlueButton variant="link" className="p-2" {...props}>
<ButtonIcon as={getIcon(icon)} size={size} />
</GlueButton>
);
};
2.4 Other Form Components
| Component | Approach |
|---|---|
Switch | Direct mapping with className styling |
Checkbox | Direct mapping |
RadioButton | Use Gluestack Radio group |
Searchbar | Compose from Input + InputSlot + Icon |
Deliverables
- Button, IconButton components with Paper-like API
- TextInput with label, error, helperText support
- Switch, Checkbox, Radio components
- Searchbar composite component
- All form screens updated and working
Phase 3: Overlay Components (Week 5-6)
Goals
- Migrate modal and overlay components
- Implement Portal-based overlays
- Update toast notification system
Tasks
3.1 Modal/Dialog Migration
Before (Paper):
<Portal>
<Dialog visible={visible} onDismiss={hide}>
<Dialog.Title>Confirm</Dialog.Title>
<Dialog.Content>
<Text>Are you sure?</Text>
</Dialog.Content>
<Dialog.Actions>
<Button onPress={hide}>Cancel</Button>
<Button onPress={confirm}>OK</Button>
</Dialog.Actions>
</Dialog>
</Portal>
After (Gluestack wrapper):
// @digiwedge/gluestack-ui/components/overlays/Dialog.tsx
export const Dialog = ({ visible, onDismiss, children }) => {
return (
<AlertDialog isOpen={visible} onClose={onDismiss}>
<AlertDialogBackdrop />
<AlertDialogContent>
{children}
</AlertDialogContent>
</AlertDialog>
);
};
Dialog.Title = ({ children }) => (
<AlertDialogHeader>
<Heading>{children}</Heading>
</AlertDialogHeader>
);
Dialog.Content = ({ children }) => (
<AlertDialogBody>{children}</AlertDialogBody>
);
Dialog.Actions = ({ children }) => (
<AlertDialogFooter>{children}</AlertDialogFooter>
);
3.2 Menu Migration
Before (Paper):
<Menu
visible={visible}
onDismiss={closeMenu}
anchor={<IconButton icon="dots-vertical" onPress={openMenu} />}
>
<Menu.Item onPress={edit} title="Edit" />
<Menu.Item onPress={delete} title="Delete" />
</Menu>
After (Gluestack wrapper):
// @digiwedge/gluestack-ui/components/overlays/Menu.tsx
export const Menu = ({ visible, onDismiss, anchor, children }) => {
return (
<GlueMenu
isOpen={visible}
onClose={onDismiss}
trigger={(props) => cloneElement(anchor, props)}
>
{children}
</GlueMenu>
);
};
Menu.Item = ({ title, onPress, ...props }) => (
<MenuItem onPress={onPress} {...props}>
<MenuItemLabel>{title}</MenuItemLabel>
</MenuItem>
);
3.3 Toast Migration
import { useToast } from '@digiwedge/gluestack-ui';
const { showToast } = useToast();
showToast({
type: 'success',
text1: 'Saved',
text2: 'Your changes are live.',
});
Deliverables
- Dialog/AlertDialog with compound API
- Menu with Menu.Item pattern
- Portal component
- Popover, Tooltip components
- Toast notification system
- All modal screens migrated
Phase 4: Business Components (Week 7-8)
Goals
- Migrate all domain-specific components
- Update auth flows
- Migrate booking and profile screens
Tasks
4.1 Auth Components
| Component | Files | Notes |
|---|---|---|
| LoginModal | 1 | Form inputs, social buttons |
| SignUpView | 1 | Multi-step form |
| ResetPasswordModal | 1 | Email input, confirmation |
| ConfirmOTPModal | 1 | OTP input, timer |
| MFAMessagingModal | 1 | Method selection |
| FullScreenAuthModal | 1 | Container wrapper |
4.2 Profile Components
| Component | Files | Notes |
|---|---|---|
| ProfileSettings | 1 | Settings list, switches |
| EditProfileView | 1 | Form with avatar |
| ProfileAvatar | 1 | Avatar with camera |
| MyBuddies | 1 | List with actions |
4.3 Booking Components
| Component | Files | Notes |
|---|---|---|
| BookingInformationModal | 1 | Read-only display |
| DeletePaymentCardModal | 1 | Confirmation dialog |
| MakeCardDefault | 1 | Selection modal |
| ClubContactForm | 1 | Contact form |
4.4 Buddy Management
| Component | Files | Notes |
|---|---|---|
| BuddyList | 1 | Scrollable list |
| BuddyInputModal | 1 | Add buddy form |
| PlayerSearchModal | 1 | Search + select |
| DeleteBuddyModal | 1 | Confirmation |
| AddPlayersFromBuddies | 1 | Multi-select |
Deliverables
- All auth screens functional
- Profile management working
- Booking modals migrated
- Buddy management complete
- All business logic preserved
Phase 5: App Integration & QA (Week 9-10)
Goals
- Remove react-native-paper dependency
- Update all app entry points
- Comprehensive visual QA
- Performance validation
Tasks
5.1 Remove Paper Dependencies
# Remove from each app
pnpm remove react-native-paper --filter teetime-mobile
pnpm remove react-native-paper --filter teetime-country-club
pnpm remove react-native-paper --filter scl-mobile
pnpm remove react-native-paper --filter messaging_mobile
# Remove from root
pnpm remove react-native-paper -w
# Update workspace
pnpm install
5.2 Update App Providers
Before:
import { PaperProvider, MD3LightTheme } from 'react-native-paper';
export default function App() {
return (
<PaperProvider theme={MD3LightTheme}>
<NavigationContainer>
{/* ... */}
</NavigationContainer>
</PaperProvider>
);
}
After:
import { GluestackUIProvider } from '@digiwedge/gluestack-ui';
import '../global.css';
export default function App() {
return (
<GluestackUIProvider>
<NavigationContainer>
{/* ... */}
</NavigationContainer>
</GluestackUIProvider>
);
}
5.3 Visual QA Checklist
| Screen | teetime-mobile | teetime-country-club | scl-mobile | messaging |
|---|---|---|---|---|
| Login | [ ] | [ ] | [ ] | [ ] |
| Signup | [ ] | [ ] | [ ] | [ ] |
| Home | [ ] | [ ] | [ ] | [ ] |
| Profile | [ ] | [ ] | [ ] | [ ] |
| Settings | [ ] | [ ] | [ ] | [ ] |
| Booking | [ ] | [ ] | N/A | N/A |
| Buddies | [ ] | [ ] | N/A | N/A |
5.4 Performance Validation
- Measure initial bundle size (before/after)
- Measure TTI (Time to Interactive)
- Profile render performance
- Test on low-end devices
Deliverables
- Zero react-native-paper imports
- All apps building successfully
- Visual parity with before-state
- Performance metrics documented
- Dev clients rebuilt for all apps
Rollback Plan
If critical issues are discovered post-migration:
- Git Revert: All changes are in a single branch, easy to revert
- Package Restore: Re-add react-native-paper dependencies
- Provider Swap: Switch back to PaperProvider
# Emergency rollback
git revert --no-commit HEAD~N..HEAD
pnpm add react-native-paper@5.14.5 -w
pnpm install
Success Metrics
| Metric | Target | Measurement |
|---|---|---|
| Build Success | 100% | All 4 apps build |
| Test Pass Rate | 100% | Existing tests pass |
| Bundle Size | No more than current | Bundle analyzer |
| Visual Parity | 95%+ | Screenshot diff |
| TTI | No more than current | Performance profiling |
| Paper Imports | 0 | grep audit |
Post-Migration Tasks
- Delete
libs/ui/react-native-paper/directory - Update CI/CD pipelines
- Update developer documentation
- Archive this migration epic
- Communicate to team