A modern React Native wizard component powered by Zustand for building complex, multi-step flows with lightweight, efficient state management.
- π― Type-safe wizard implementation with full TypeScript support
- β‘ Zustand-based state management for optimal performance
- π Flexible step configuration and navigation
- π¨ Customizable UI components and styling
- π Step context for managing step-specific state and actions
- π§ Navigation context for custom navigation and indicators
- βΏ Accessibility support built-in
- π§ͺ Comprehensive test coverage
- π Lightweight bundle with minimal dependencies
- π§ DevTools support for easy debugging
npm install @zestic/react-native-zustand-wizard
# or
yarn add @zestic/react-native-zustand-wizard
import { Wizard } from '@zestic/react-native-zustand-wizard';
import { Step } from '@zestic/react-native-zustand-wizard/types';
const steps: Step[] = [
{
id: 'step1',
component: Step1Component,
order: 1,
canMoveNext: true,
nextLabel: 'Continue',
previousLabel: 'Go Back',
},
{
id: 'step2',
component: Step2Component,
order: 2,
},
];
const MyWizard = () => (
<Wizard
steps={steps}
nextLabel="Next"
previousLabel="Back"
finishLabel="Done"
/>
);
import { useStepContext } from '@zestic/react-native-zustand-wizard/utils/wizardUtils';
const Step1Component = () => {
const { updateField, getStepData, canMoveNext } = useStepContext('step1');
const data = getStepData();
// Enable/disable next button based on form validation
React.useEffect(() => {
const isValid = /* your validation logic */;
canMoveNext(isValid);
}, [data, canMoveNext]);
return (
<View>
<TextInput
value={data?.name || ''}
onChangeText={(text) => updateField('name', text)}
/>
</View>
);
};
import { WizardNavigation } from '@zestic/react-native-zustand-wizard/components/navigation';
import { useNavigationContext } from '@zestic/react-native-zustand-wizard/utils/wizardUtils';
// Custom Navigation Component
const CustomNavigation = () => {
const {
currentStepPosition,
totalSteps,
isNextDisabled,
isPreviousHidden,
nextLabel,
previousLabel,
onNext,
onPrevious,
} = useNavigationContext();
return (
<View>
<StepIndicator
currentStep={currentStepPosition}
totalSteps={totalSteps}
/>
<View>
{!isPreviousHidden && (
<Button title={previousLabel} onPress={onPrevious} />
)}
<Button title={nextLabel} onPress={onNext} disabled={isNextDisabled} />
</View>
</View>
);
};
// Using Custom Navigation
const MyWizard = () => (
<Wizard steps={steps} renderNavigation={(store) => <CustomNavigation />} />
);
import { StepIndicator } from '@zestic/react-native-zustand-wizard/components/navigation';
import { useNavigationContext } from '@zestic/react-native-zustand-wizard/utils/wizardUtils';
const CustomStepIndicator = () => {
const { currentStepPosition, totalSteps } = useNavigationContext();
return (
<View>
{Array.from({ length: totalSteps }, (_, i) => i + 1).map((step) => (
<View
key={step}
style={[
styles.step,
step === currentStepPosition && styles.activeStep,
step < currentStepPosition && styles.completedStep,
]}
/>
))}
</View>
);
};
You'll need to set up a development build for Storybook development:
# Install dependencies
yarn install
# Start Storybook
yarn storybook
# For web version
yarn storybook:web
# For iOS (requires development build)
yarn storybook:ios
# For Android (requires development build)
yarn storybook:android
yarn test
# Run ESLint
yarn lint
# Format code with Prettier
yarn format
# Check formatting without making changes
yarn format:check
yarn build