Forms are used to collect user inputs on web applications. However, you may find yourself in situations where you need to collect large amounts of information from the user that may result in an overwhelming number of fields.
One solution is to break the form into multiple sections (or steps). Each section collecting a certain type of information at each point.
In this tutorial, you will build a multistep registration form with React and Semantic UI. You can achieve this by using React components for each section. You can then choose which components are rendered at each step by manipulating state.
To complete this tutorial, you will need:
This tutorial was verified with Node v14.0.0, npm
v6.14.4, react
v16.13.1, semantic-ui-react
v0.88.2, and semantic-ui-css
v2.4.1.
First, you will use npx
to use create-react-app
to generate your project.
In your terminal, run the following:
- npx create-react-app multistep-form
This will create a sample React application with the development environment fully configured.
Next, change into the newly created project directory:
- cd multistep-form
For styling, you will use the UI framework, Semantic UI. To use Semantic UI in your React application, you will useSemantic UI React. Semantic UI React provides prebuilt components that you can use to speed up the development process.
It also supports responsive web design which makes it great for building cross-platform websites.
In your terminal, run the following:
- npm install semantic-ui-react@0.88.2
Next, you will also include the default theme.
In your terminal, run the following:
- npm install semantic-ui-css@2.4.1
To add the custom Semantic UI CSS, open the index.js
file:
- nano src/index.js
And add the following import
:
import React from 'react';
import ReactDOM from 'react-dom';
import 'semantic-ui-css/semantic.min.css';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
Now that the React project is initialized and you have added the necessary dependencies you can start creating your components.
In this tutorial step, you will be adding creating five components:
MainForm.jsx
UserDetails.jsx
PersonalDetails.jsx
Confirmation.jsx
Success.jsx
First, let’s edit App.js
to import the components and render them:
- nano src/App.js
Replace all of the boilerplate code generated by create-react-app
with the following lines of code:
import React, { Component } from 'react';
import { Grid } from 'semantic-ui-react';
import './App.css';
import MainForm from './components/MainForm';
class App extends Component {
render() {
return (
<Grid verticalAlign='middle' style={{ height: '100vh' }}>
<MainForm />
</Grid>
);
}
}
export default App;
Note: If you start the development server now you will encounter errors until you have finished writing and importing all of the components for the form.
In this code, you have used the Grid
component from Semantic UI React which makes it more presentable by centering the text and adding padding.
You have also imported and used the MainForm
component. Let’s work on that now.
MainForm
ComponentThe first component you will set up is the MainForm.jsx
component which will be in charge of most of the functionality in your application.
Let’s create a components
folder under the src
directory:
- mkdir src/components
And then create a Mainform.jsx
file:
- nano src/components/MainForm.jsx
Add the following lines of code:
import React, { Component } from 'react';
import UserDetails from './UserDetails';
import PersonalDetails from './PersonalDetails';
import Confirmation from './Confirmation';
import Success from './Success';
This code will import the dependencies. It will also import the four sections of the form: UserDetails
, PersonalDetails
, Confirmation
, and Success
. You will be building these components next.
Then, underneath the imports, add state for the MainForm
component:
// ...
class MainForm extends Component {
state = {
step: 1,
firstName: '',
lastName: '',
email: '',
age: '',
city: '',
country: ''
}
}
export default MainForm;
firstName
, lastName
, email
, age
, city
, and country
are the fields of information you are interested in end users providing.
step
will be a number from 1
to 4
. This is will allow you to track which step in the multistep process the user currently is.
Then, add the nextStep
, prevStep
, and handleChange
functions:
// ...
class MainForm extends Component {
// ...
nextStep = () => {
const { step } = this.state
this.setState({
step : step + 1
})
}
prevStep = () => {
const { step } = this.state
this.setState({
step : step - 1
})
}
handleChange = input => event => {
this.setState({[input]: event.target.value})
}
}
// ...
The component is initialized with the default value for step in state as 1
, and the first section of your form is rendered. The user can then skip back and forth between steps using the prevStep
and nextStep
functions. These update the value of step
in state so as to allow the user to switch between the rendered components.
The handleChange
function updates the value of the details provided by the user inside state and like the prevStep
and nextStep
functions, it will be passed to the child components as props. This will allow you to pass the functionality implemented for use within the child components.
Then, add the render
function:
// ...
class MainForm extends Component {
// ...
render() {
const { step } = this.state;
const { firstName, lastName, email, age, city, country } = this.state;
const values = { firstName, lastName, email, age, city, country };
switch(step) {
case 1:
return <UserDetails
nextStep={this.nextStep}
handleChange = {this.handleChange}
values={values}
/>
case 2:
return <PersonalDetails
nextStep={this.nextStep}
prevStep={this.prevStep}
handleChange = {this.handleChange}
values={values}
/>
case 3:
return <Confirmation
nextStep={this.nextStep}
prevStep={this.prevStep}
values={values}
/>
case 4:
return <Success />
}
}
}
// ...
The multistep form is using the switch
statement, which reads the step
from state and uses this to select which components are rendered at each step.
At Step 1, the UserDetails
component will be rendered. At Step 2, the PersonalDetails
component will be rendered. At Step 3, the Confirmation
component will be rendered. And at Step 4, the Success
component will be rendered.
Your MainForm
component is now complete.
UserDetails
ComponentNow, let’s create the first section of the form.
Create a UserDetails.jsx
file:
- nano src/components/UserDetails.jsx
Add the following lines of code:
import React, { Component } from 'react';
import { Grid, Header, Segment, Form, Button } from 'semantic-ui-react';
class UserDetails extends Component {
saveAndContinue = (e) => {
e.preventDefault();
this.props.nextStep();
}
render() {
const { values } = this.props;
return (
<Grid.Column style={{ maxWidth: 450 }}>
<Header textAlign='center'>
<h1>Enter User Details</h1>
</Header>
<Form>
<Segment>
<Form.Field>
<label>First Name</label>
<input
placeholder='First Name'
onChange={this.props.handleChange('firstName')}
defaultValue={values.firstName}
/>
</Form.Field>
<Form.Field>
<label>Last Name</label>
<input
placeholder='Last Name'
onChange={this.props.handleChange('lastName')}
defaultValue={values.lastName}
/>
</Form.Field>
<Form.Field>
<label>Email Address</label>
<input
type='email'
placeholder='Email Address'
onChange={this.props.handleChange('email')}
defaultValue={values.email}
/>
</Form.Field>
</Segment>
<Segment>
<Button onClick={this.saveAndContinue}>Save And Continue</Button>
</Segment>
</Form>
</Grid.Column>
);
}
}
export default UserDetails;
This creates a form that collects the user’s first name, last name, and email address.
The saveAndContinue
function is then in charge of routing users to the next component once they are done filling in the details.
You will notice that you have called the nextStep
function which you had provided to the component as props. Each time this function is called, it updates the state of the parent component (MainForm
). You also called handleChange
and provided the name of each field to be updated on each input element.
You may have also noticed that each input field is provided a defaultValue
which it picks from the state of the MainForm
component. This allows it to pick the updated value in state in cases where the user routes back to one step from another.
Your UserDetails
component is now complete.
PersonalDetails
ComponentNow, let’s create your second section, which collects the user’s personal information.
Create a PersonalDetails.jsx
file:
- nano src/components/PersonalDetails.jsx
Add the following lines of code:
import React, { Component } from 'react';
import { Grid, Header, Segment, Form, Button } from 'semantic-ui-react';
class PersonalDetails extends Component {
saveAndContinue = (e) => {
e.preventDefault();
this.props.nextStep();
}
back = (e) => {
e.preventDefault();
this.props.prevStep();
}
render() {
const { values } = this.props;
return (
<Grid.Column style={{ maxWidth: 450 }}>
<Header textAlign='center'>
<h1>Enter Personal Details</h1>
</Header>
<Form>
<Segment>
<Form.Field>
<label>Age</label>
<input placeholder='Age'
onChange={this.props.handleChange('age')}
defaultValue={values.age}
/>
</Form.Field>
<Form.Field>
<label>City</label>
<input placeholder='City'
onChange={this.props.handleChange('city')}
defaultValue={values.city}
/>
</Form.Field>
<Form.Field>
<label>Country</label>
<input placeholder='Country'
onChange={this.props.handleChange('country')}
defaultValue={values.country}
/>
</Form.Field>
</Segment>
<Segment textAlign='center'>
<Button onClick={this.back}>Back</Button>
<Button onClick={this.saveAndContinue}>Save And Continue</Button>
</Segment>
</Form>
</Grid.Column>
)
}
}
export default PersonalDetails;
This section collects the user’s age and location. The overall functionality is similar to the user details section apart from the addition of the Back button which will take users back to the previous step by calling prevStep
from props.
You have the back
and saveAndContinue
functions in each of your components take an event(e)
as an argument and you call event.preventDefault()
in these functions to stop the form from reloading each time users submit which is the default behavior in forms.
Your PersonalDetails
component is now complete.
Confirmation
ComponentNow, let’s create the final section of your form where the user confirms the details they have fed the application are correct.
Create a Confirmation.jsx
file:
- nano src/components/Confirmation.jsx
Add the following lines of code:
import React, { Component } from 'react';
import { Grid, Header, Segment, Button, List } from 'semantic-ui-react';
class Confirmation extends Component {
saveAndContinue = (e) => {
e.preventDefault();
this.props.nextStep();
}
back = (e) => {
e.preventDefault();
this.props.prevStep();
}
render() {
const { values: { firstName, lastName, email, age, city, country } } = this.props;
return (
<Grid.Column style={{ maxWidth: 450 }}>
<Header textAlign='center'>
<h1>Confirm your Details</h1>
<p>Click Confirm if the following details have been correctly entered</p>
</Header>
<Segment>
<List divided relaxed>
<List.Item>
<List.Icon name='users' size='large' />
<List.Content>First Name: {firstName}</List.Content>
</List.Item>
<List.Item>
<List.Icon name='users' size='large' />
<List.Content>Last Name: {lastName}</List.Content>
</List.Item>
<List.Item>
<List.Icon name='mail' size='large' />
<List.Content>Email: {email}</List.Content>
</List.Item>
<List.Item>
<List.Icon name='calendar' size='large' />
<List.Content>Age: {age}</List.Content>
</List.Item>
<List.Item>
<List.Icon name='marker' size='large' />
<List.Content>Location: {city}, {country}</List.Content>
</List.Item>
</List>
</Segment>
<Segment textAlign='center'>
<Button onClick={this.back}>Back</Button>
<Button onClick={this.saveAndContinue}>Confirm</Button>
</Segment>
</Grid.Column>
)
}
}
export default Confirmation;
This creates a section that displays all the details entered by the user and asks them to confirm the details before submitting them. This is typically where you would make the final API calls to the backend to submit and save the data.
Since this is a demo, the Confirm
button implements the same saveAndContinue
function you used in previous components. However, in a real-world scenario, you would implement a different submit function that would handle the final submission and save the data.
Your Confirmation
component is now complete.
Success
ComponentThe next and final component in your project is the Success
component which would be rendered when the user successfully saves their information.
Create a Confirmation.jsx
file:
- nano src/components/Success.jsx
Add the following lines of code:
import React, { Component } from 'react';
import { Grid, Header } from 'semantic-ui-react';
class Success extends Component {
render() {
return (
<Grid.Column style={{ maxWidth: 450 }}>
<Header textAlign='center'>
<h1>Details Successfully Saved</h1>
</Header>
</Grid.Column>
)
}
}
export default Success;
This will display 'Details Successfully Saved'
message when the user reaches the final step of the form.
You have completed all the components for your multistep form.
Now, view the form in your web browser.
Use a terminal window and ensure that you are in the project directory. Start the project with the following command:
- npm start
Navigate to localhost:3000
in your web browser.
Your application will display Step 1 - Enter User Details:
After entering your information and clicking Save and Continue, you will be directed to Step 2 - Enter Personal Details:
After entering your information and clicking Save and Continue, you will be directed to Step 3 - Confirm Your Details:
After clicking Confirm, you will be directed to Step 4 - Success.
You now have a working multistep form in React.
Multistep forms are great when you need to break down a form into sections. They can also allow you to carry out form validation at each step before carrying on to the next step.
The use of a switch
statement for routing between steps eliminates the need for a router which means you can plug the form into other components without having to adjust routing.
It is also possible to set up API calls in between steps if necessary which opens up a number of new possibilities.
You can also handle the state better by adding state management tools such as Redux.
If you’d like to learn more about React, check out our React topic page for exercises and programming projects.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!