@brijen/react-native-multistep

1.0.0 • Public • Published

@brijen/react-native-multistep

A lightweight multi-step view component for React Native with smooth transitions using Reanimated

video 1 video 2 video 3

Table of Contents

  1. Installation
  2. Usage
  3. Customizing and Styling
  4. Custom Components
  5. Using react-hook-form with @brijen/react-native-multistep
  6. MultiStepProps
  7. StepProps
  8. MultiStepRef
  9. Contributing
  10. License

Installation

npm install @brijen/react-native-multistep

Peer Dependencies

This package relies on the following peer dependencies. Make sure they are installed in your project:

  1. react-native-reanimated This library is used for smooth animations and transitions. Install it using:

    npm install react-native-reanimated

    Follow the official installation guide to set it up correctly.

  2. react-native-svg This library is used for rendering SVG graphics. Install it using:

    npx expo install react-native-svg

    or

    npm install react-native-svg

    Follow the official installation guide to configure it properly.

Notes:

  • Ensure that these libraries are installed and linked correctly in your React Native project.
  • If you encounter any issues, refer to the official documentation for each library.

Usage

import { MultiStep, Step } from '@brijen/react-native-multistep';
import { Text } from 'react-native';

const App = () => {
  return (
    <MultiStep
      fullScreenHeight
      onFinalStepSubmit={() => console.log('Submitted')}
    >
      <Step title="Step 1">
        <Text>Content for Step 1</Text>
      </Step>
      <Step title="Step 2">
        <Text>Content for Step 2</Text>
      </Step>
    </MultiStep>
  );
};

export default App;

Example: Using Input Fields with MultiStep

Here's an example of how to apply custom styling to the MultiStep and Step components:

video 1 video 2 video 3
import { useState } from 'react';
import { Text, TextInput, StyleSheet } from 'react-native';
import { MultiStep, Step } from '@brijen/react-native-multistep';

const App = () => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    address: '',
    city: '',
    cardNumber: '',
  });

  const handleChange = (field: string, value: string) => {
    setFormData((prev) => ({ ...prev, [field]: value }));
  };

  const handleSubmit = () => {
    console.log('Final Form Data:', formData);
  };

  return (
    <MultiStep
      onFinalStepSubmit={handleSubmit}
      tintColor="#007AFF"
      fullScreenHeight
    >
      <Step title="Personal Info" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Full Name</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your name"
          value={formData.name}
          onChangeText={(text) => handleChange('name', text)}
        />
        <Text style={styles.label}>Email Address</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your email"
          value={formData.email}
          keyboardType="email-address"
          onChangeText={(text) => handleChange('email', text)}
        />
      </Step>

      <Step title="Shipping Address" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Street Address</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your address"
          value={formData.address}
          onChangeText={(text) => handleChange('address', text)}
        />
        <Text style={styles.label}>City</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your city"
          value={formData.city}
          onChangeText={(text) => handleChange('city', text)}
        />
      </Step>

      <Step title="Payment Information" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Card Number</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter card number"
          value={formData.cardNumber}
          keyboardType="numeric"
          onChangeText={(text) => handleChange('cardNumber', text)}
        />
      </Step>
    </MultiStep>
  );
};

export default App;

const styles = StyleSheet.create({
  stepContent: {
    gap: 10,
  },
  label: {
    fontSize: 16,
    marginBottom: 8,
  },
  input: {
    height: 40,
    borderWidth: 1,
    borderColor: '#007AFF',
    borderRadius: 8,
    paddingHorizontal: 10,
    backgroundColor: '#FFF',
  },
});

Customizing and Styling

You can customize the styling of the MultiStep and Step components to match your app's aesthetics. The provided table lists all the available properties that you can use to adjust the appearance and behavior of the multi-step process. This includes customizing button styles, text styles, progress indicators, and container styles.

Example: Custom Styling

Here's an example of how to apply custom styling to the MultiStep and Step components:

video 1 video 2 video 3
import { useState } from 'react';
import { Text, TextInput, StyleSheet } from 'react-native';
import { MultiStep, Step } from '@brijen/react-native-multistep';

const App = () => {
  const [formData, setFormData] = useState({
    username: '',
    phone: '',
    country: '',
    postalCode: '',
    cardHolder: '',
    expiryDate: '',
  });

  const handleChange = (field: string, value: string) => {
    setFormData((prev) => ({ ...prev, [field]: value }));
  };

  const handleSubmit = () => {
    console.log('Submitted Data:', formData);
  };

  return (
    <MultiStep
      onFinalStepSubmit={handleSubmit}
      nextButtonText="Continue"
      prevButtonText="Go Back"
      submitButtonText="Complete"
      nextButtonStyle={styles.nextButton}
      prevButtonStyle={styles.prevButton}
      submitButtonStyle={styles.submitButton}
      nextButtonTextStyle={styles.nextButtonText}
      prevButtonTextStyle={styles.prevButtonText}
      submitButtonTextStyle={styles.submitButtonText}
      progressCircleSize={70}
      progressCircleThickness={6}
      progressCircleColor="#e290a6"
      progressCircleTrackColor="#D3D3D3"
      progressCircleLabelStyle={styles.progressText}
      globalStepTitleStyle={styles.stepTitle}
      globalNextStepTitleStyle={styles.nextStepTitle}
      fullScreenHeight
    >
      <Step title="User Details" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Username</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your username"
          value={formData.username}
          onChangeText={(text) => handleChange('username', text)}
        />
        <Text style={styles.label}>Phone Number</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your phone number"
          value={formData.phone}
          keyboardType="phone-pad"
          onChangeText={(text) => handleChange('phone', text)}
        />
      </Step>

      <Step title="Location Info" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Country</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your country"
          value={formData.country}
          onChangeText={(text) => handleChange('country', text)}
        />
        <Text style={styles.label}>Postal Code</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter postal code"
          value={formData.postalCode}
          keyboardType="numeric"
          onChangeText={(text) => handleChange('postalCode', text)}
        />
      </Step>

      <Step title="Payment Info" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Cardholder Name</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter cardholder's name"
          value={formData.cardHolder}
          onChangeText={(text) => handleChange('cardHolder', text)}
        />
        <Text style={styles.label}>Expiry Date</Text>
        <TextInput
          style={styles.input}
          placeholder="MM/YY"
          value={formData.expiryDate}
          keyboardType="numeric"
          onChangeText={(text) => handleChange('expiryDate', text)}
        />
      </Step>
    </MultiStep>
  );
};

export default App;

const styles = StyleSheet.create({
  stepContent: {
    gap: 12,
  },
  label: {
    fontSize: 16,
    marginBottom: 8,
    fontWeight: '500',
  },
  input: {
    height: 42,
    borderWidth: 1,
    borderColor: '#A6B1E1',
    borderRadius: 8,
    paddingHorizontal: 10,
    backgroundColor: '#FFF',
  },
  stepTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#36cab2',
  },
  nextStepTitle: {
    color: '#696969',
  },
  progressText: {
    fontSize: 14,
    fontWeight: '600',
    color: '#e290a6',
  },
  nextButton: {
    backgroundColor: '#d7b665',
    paddingVertical: 12,
    borderRadius: 8,
  },
  nextButtonText: {
    color: '#FFF',
    fontSize: 16,
  },
  prevButton: {
    paddingVertical: 12,
    borderRadius: 8,
    borderColor: '#d7b665',
  },
  prevButtonText: {
    color: '#000',
    fontSize: 16,
  },
  submitButton: {
    backgroundColor: '#1E3E62',
    paddingVertical: 12,
    borderRadius: 8,
  },
  submitButtonText: {
    color: '#FFF',
    fontSize: 16,
  },
});

Custom Components

You can also provide custom components for titles, buttons, and much more. This allows for greater flexibility and customization to match your app's design and functionality.

Example: Custom Components

Here's an example of how to use custom components for the step titles and buttons:

video 1 video 2 video 3
import { useState, useRef } from 'react';
import {
  Text,
  TextInput,
  StyleSheet,
  View,
  TouchableOpacity,
} from 'react-native';
import { MultiStep, Step, type MultiStepRef } from '@brijen/react-native-multistep';
import { FontAwesome, MaterialIcons } from '@expo/vector-icons';

const App = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
    age: '',
  });

  const handleChange = (field: string, value: string) => {
    setFormData((prev) => ({ ...prev, [field]: value }));
  };

  const handleSubmit = () => {
    console.log('Final Form Data:', formData);
  };

  const ref = useRef<MultiStepRef>(null);

  return (
    <MultiStep
      onFinalStepSubmit={handleSubmit}
      tintColor="#AD49E1"
      progressCircleColor="#FFAF00"
      nextButtonComponent={
        <NextButton onPress={() => ref.current?.nextStep()} />
      }
      prevButtonComponent={
        <PrevButton onPress={() => ref.current?.prevStep()} />
      }
      submitButtonComponent={<SubmitButton onPress={handleSubmit} />}
      ref={ref}
    >
      <Step
        title="User Info"
        titleComponent={<StepTitle title="User Info" icon="user" />}
        stepContainerStyle={styles.stepContent}
      >
        <Text style={styles.label}>Username</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your username"
          value={formData.username}
          onChangeText={(text) => handleChange('username', text)}
        />
        <Text style={styles.label}>Email</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your email"
          value={formData.email}
          keyboardType="email-address"
          onChangeText={(text) => handleChange('email', text)}
        />
      </Step>

      <Step
        title="Security"
        titleComponent={<StepTitle title="Security" icon="lock" />}
        stepContainerStyle={styles.stepContent}
      >
        <Text style={styles.label}>Password</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter password"
          secureTextEntry
          value={formData.password}
          onChangeText={(text) => handleChange('password', text)}
        />
      </Step>

      <Step
        title="Additional Info"
        titleComponent={<StepTitle title="Additional Info" icon="info" />}
        stepContainerStyle={styles.stepContent}
      >
        <Text style={styles.label}>Age</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter your age"
          keyboardType="numeric"
          value={formData.age}
          onChangeText={(text) => handleChange('age', text)}
        />
      </Step>
    </MultiStep>
  );
};

export default App;

const StepTitle = ({
  title,
  icon,
}: {
  title: string;
  icon: 'user' | 'lock' | 'info';
}) => (
  <View style={styles.stepTitleContainer}>
    <FontAwesome name={icon} size={20} color="#000" />
    <Text style={styles.stepTitle}>{title}</Text>
  </View>
);

const NextButton = ({ onPress }: { onPress: () => void }) => (
  <TouchableOpacity style={styles.nextButton} onPress={onPress}>
    <Text style={styles.buttonText}>Next</Text>
    <MaterialIcons name="arrow-forward-ios" size={18} color="white" />
  </TouchableOpacity>
);

const PrevButton = ({ onPress }: { onPress: () => void }) => (
  <TouchableOpacity style={styles.prevButton} onPress={onPress}>
    <MaterialIcons name="arrow-back-ios" size={18} color="white" />
    <Text style={styles.buttonText}>Back</Text>
  </TouchableOpacity>
);

const SubmitButton = ({ onPress }: { onPress: () => void }) => (
  <TouchableOpacity style={styles.submitButton} onPress={onPress}>
    <Text style={styles.buttonText}>Submit</Text>
    <FontAwesome name="check-circle" size={18} color="white" />
  </TouchableOpacity>
);

const styles = StyleSheet.create({
  stepContent: {
    gap: 12,
  },
  label: {
    fontSize: 16,
    marginBottom: 8,
    fontWeight: '500',
  },
  input: {
    height: 42,
    borderWidth: 1,
    borderColor: '#5CA9FF',
    borderRadius: 8,
    paddingHorizontal: 10,
    backgroundColor: '#FFF',
  },
  stepTitleContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 8,
  },
  stepTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#7C00FE',
  },
  nextButton: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 5,
    backgroundColor: '#F5004F',
    paddingVertical: 12,
    borderRadius: 8,
    width: 100,
  },
  prevButton: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 5,
    backgroundColor: '#B6CBBD',
    paddingVertical: 12,
    borderRadius: 8,
    width: 100,
  },
  submitButton: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 5,
    backgroundColor: '#16C47F',
    paddingVertical: 12,
    borderRadius: 8,
    width: 100,
  },
  buttonText: {
    fontSize: 16,
    color: 'white',
    fontWeight: '600',
  },
});

Using react-hook-form with @brijen/react-native-multistep

You can integrate react-hook-form with @brijen/react-native-multistep to manage form state and validation across multiple steps. Below is an example demonstrating how to use react-hook-form with @brijen/react-native-multistep.

Example: Integrating react-hook-form

import { useForm, Controller } from 'react-hook-form';
import { Text, TextInput, StyleSheet } from 'react-native';
import { MultiStep, Step } from '@brijen/react-native-multistep';

const App = () => {
  const { control, handleSubmit } = useForm({
    defaultValues: {
      name: '',
      email: '',
      address: '',
      city: '',
      cardNumber: '',
    },
  });

  const onSubmit = (data: any) => {
    console.log('Final Form Data:', data);
  };

  return (
    <MultiStep
      onFinalStepSubmit={handleSubmit(onSubmit)}
      tintColor="#DA498D"
      fullScreenHeight
    >
      <Step title="Personal Info" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Full Name</Text>
        <Controller
          control={control}
          name="name"
          render={({ field: { onChange, value } }) => (
            <TextInput
              style={styles.input}
              placeholder="Enter your name"
              value={value}
              onChangeText={onChange}
            />
          )}
        />
        <Text style={styles.label}>Email Address</Text>
        <Controller
          control={control}
          name="email"
          render={({ field: { onChange, value } }) => (
            <TextInput
              style={styles.input}
              placeholder="Enter your email"
              value={value}
              keyboardType="email-address"
              onChangeText={onChange}
            />
          )}
        />
      </Step>

      <Step title="Shipping Address" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Street Address</Text>
        <Controller
          control={control}
          name="address"
          render={({ field: { onChange, value } }) => (
            <TextInput
              style={styles.input}
              placeholder="Enter your address"
              value={value}
              onChangeText={onChange}
            />
          )}
        />
        <Text style={styles.label}>City</Text>
        <Controller
          control={control}
          name="city"
          render={({ field: { onChange, value } }) => (
            <TextInput
              style={styles.input}
              placeholder="Enter your city"
              value={value}
              onChangeText={onChange}
            />
          )}
        />
      </Step>

      <Step title="Payment Information" stepContainerStyle={styles.stepContent}>
        <Text style={styles.label}>Card Number</Text>
        <Controller
          control={control}
          name="cardNumber"
          render={({ field: { onChange, value } }) => (
            <TextInput
              style={styles.input}
              placeholder="Enter card number"
              value={value}
              keyboardType="numeric"
              onChangeText={onChange}
            />
          )}
        />
      </Step>
    </MultiStep>
  );
};

export default App;

const styles = StyleSheet.create({
  stepContent: {
    gap: 10,
  },
  label: {
    fontSize: 16,
    marginBottom: 8,
  },
  input: {
    height: 40,
    borderWidth: 1,
    borderColor: '#DA498D',
    borderRadius: 8,
    paddingHorizontal: 10,
    backgroundColor: '#FFF',
  },
});

MultiStepProps

The MultiStepProps interface defines the properties for the MultiStep component, which handles multi-step navigation.

Property Type Required Default Value Description
children React.ReactNode Yes - The steps or content to be rendered inside the multi-step view. This can be one or more Step components.
onFinalStepSubmit () => void No - Callback function that is called when the user presses the submit button on the last step.
prevButtonText string No "Back" Text for the "Previous" button.
nextButtonText string No "Next" Text for the "Next" button.
prevButtonStyle ViewStyle No - Style for the "Previous" button container.
nextButtonStyle ViewStyle No - Style for the "Next" button container.
prevButtonTextStyle TextStyle No - Style for the text inside the "Previous" button.
nextButtonTextStyle TextStyle No - Style for the text inside the "Next" button.
prevButtonComponent JSX.Element No - Custom component to replace the default "Previous" button. Overrides prevButtonText and prevButtonStyle.
nextButtonComponent JSX.Element No - Custom component to replace the default "Next" button. Overrides nextButtonText and nextButtonStyle.
tintColor string No - Primary tint color for active indicators and buttons.
globalStepTitleStyle TextStyle No - Global style for the step title text. Accepts a React Native TextStyle object.
globalNextStepTitleStyle TextStyle No - Global style for the next step title text. Accepts a React Native TextStyle object.
progressCircleSize number No 65 The size (diameter) of the circular progress indicator in pixels.
progressCircleThickness number No 5 The thickness of the progress ring.
progressCircleColor string No "#DE3163" The color of the filled (progress) portion of the circle.
progressCircleTrackColor string No "#E0E0E0" The color of the unfilled (background) portion of the circle.
progressCircleLabelStyle TextStyle No - Style for the text inside the progress circle. Accepts a React Native TextStyle object.
headerStyle ViewStyle No - Style for the header where the title and progress bar are shown. Accepts a React Native ViewStyle object.
globalStepContainerStyle ViewStyle No - Global style for the step container. Accepts a React Native ViewStyle object.
fullScreenHeight boolean No false If true, the step will take the entire available screen height.
buttonContainerStyle ViewStyle No - Style for the button container. Accepts a React Native ViewStyle object.
submitButtonText string No "Submit" Text for the "Submit" button.
submitButtonStyle ViewStyle No - Style for the "Submit" button. Accepts a React Native ViewStyle object.
submitButtonTextStyle TextStyle No - Style for the text inside the "Submit" button. Accepts a React Native TextStyle object.
submitButtonComponent JSX.Element No - Custom component to replace the default "Submit" button. Overrides submitButtonText and submitButtonStyle.

StepProps

The StepProps interface defines the properties for a single step in a multi-step process.

Property Type Required Default Value Description
title string Yes - The title of the step. This is displayed as the step's label.
children React.ReactNode Yes - The content of the step. This can be any React component.
stepTitleStyle TextStyle No - Style for the step title text. Accepts a React Native TextStyle object.
nextStepTitleStyle TextStyle No - Style for the next step title text. Accepts a React Native TextStyle object.
titleComponent JSX.Element No - Custom component for the title. Overrides title if provided.
stepContainerStyle ViewStyle No - Style for the step container. Accepts a React Native ViewStyle object.

MultiStepRef

The MultiStepRef interface defines the methods available for controlling the MultiStep component's navigation programmatically. To use the methods available in the MultiStepRef interface, you need to pass a ref to the MultiStep component. This allows you to programmatically control the navigation between steps.

Here's an example demonstrating how to pass a ref to MultiStep and use its methods:

import { useRef } from 'react';
import { Text, View, Button } from 'react-native';
import { MultiStep, Step, type MultiStepRef } from '@brijen/react-native-multistep';

const App = () => {
  const multiStepRef = useRef<MultiStepRef>(null);

  const goToNextStep = () => {
    multiStepRef.current?.nextStep();
  };

  const goToPreviousStep = () => {
    multiStepRef.current?.prevStep();
  };

  const goToSpecificStep = (index: number) => {
    multiStepRef.current?.scrollToStep(index);
  };

  return (
    <View style={{ flex: 1 }}>
      <MultiStep ref={multiStepRef} onFinalStepSubmit={() => alert('Submitted!')}>
        <Step title="Step 1">
          <Text>Welcome to Step 1</Text>
        </Step>
        <Step title="Step 2">
          <Text>Fill in some details here.</Text>
        </Step>
        <Step title="Step 3">
          <Text>Review your information.</Text>
        </Step>
      </MultiStep>
      <View style={{ flexDirection: 'row', justifyContent: 'space-around', marginTop: 20 }}>
        <Button title="Previous" onPress={goToPreviousStep} />
        <Button title="Next" onPress={goToNextStep} />
        <Button title="Go to Step 2" onPress={() => goToSpecificStep(1)} />
      </View>
    </View>
  );
};

export default App;

In this example:

  • A ref (multiStepRef) is created using useRef and passed to the MultiStep component.
  • The goToNextStep, goToPreviousStep, and goToSpecificStep functions use the methods from the MultiStepRef interface to navigate between steps.
  • Buttons are provided to trigger these functions and demonstrate the navigation.
Method Type Description
nextStep () => void Advances to the next step in the multi-step process.
prevStep () => void Moves back to the previous step in the multi-step process.
scrollToStep (index: number) => void Scrolls to a specific step in the multi-step process.

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT

Package Sidebar

Install

npm i @brijen/react-native-multistep

Weekly Downloads

112

Version

1.0.0

License

MIT

Unpacked Size

138 kB

Total Files

59

Last publish

Collaborators

  • brijen