Creating Simple React Wizard Form

As a form grow in complexity and becoming longer and longer, your UX designer will begin to pay attention to it. Maybe even propose an idea to make it better, simply by creating a multi step form that we know as the "wizard form." That's a great idea! By only showing a few inputs on a screen, users will feel encouraged to fill the blank inputs rather than dumping it all immediately.

The question, of course, is how do you make one, with React ❓

Let me show you now.

The easiest way to create a multi step form is to create a container form element, which contains all the wizard steps component inside of it. This diagram will help you understand it clearly.

Creating React Wizard Form
Creating React Wizard Form

All three separate form components will live in one <MasterForm/> component. It will send data and functions to children components via props, and children components will trigger handleChange function to set values in state. We'll need a function to move the form from one step to another as well.

First, we'll create the form children components. This example will only include one input per form step. I will also put in comments for clarity. These children components will receive props from the parent component for value and onChange props.

/*
* Get our form steps component ready
*/

class Step1 extends Component {
  render() {
    if (this.props.currentStep !== 1) {
      return null
    } 
    return(
      <div className="form-group">
        <label htmlFor="email">Email address</label>
        <input
          className="form-control"
          id="email"
          name="email"
          type="text"
          placeholder="Enter email"
          value={this.props.email}
          onChange={this.props.handleChange}
        />
      </div>
   )
 }
}

class Step2 extends Component {
  render() {
    if (this.props.currentStep !== 2) {
      return null
    } 
    return(
    <div className="form-group">
      <label htmlFor="username">Username</label>
      <input
        className="form-control"
        id="username"
        name="username"
        type="text"
        placeholder="Enter username"
        value={this.props.username}
        onChange={this.props.handleChange}
      />
    </div>
  )
 }
}

class Step3 extends Component {
  render() {
    if (this.props.currentStep !== 3) {
      return null
    } 
    return(
    <React.Fragment>
      <div className="form-group">
        <label htmlFor="password">Password</label>
        <input
          className="form-control"
          id="password"
          name ="password"
          type="password"
          placeholder="Enter password"
          value={this.props.password}
          onChange={this.props.handleChange}
        />
      </div>     
      <button className="btn btn-success btn-block">Sign up</button>
    </React.Fragment>
  )
 }
}

Then we can put these form steps component into the master form render function and pass in the necessary props. We still use handleChange and handleSubmit from our previous form.

class MasterForm extends Component {
  constructor(props) {
    super(props)
    this.state = {
      currentStep: 1,
      email:  '',
      username: '',
      password: '', 
    }
    this.handleChange = this.handleChange.bind(this)
    this._next = this._next.bind(this)
    this._prev = this._prev.bind(this)
  }

  handleChange(event) {
    const {name, value} = event.target
    this.setState({
      [name]: value
    })    
  }
   
  handleSubmit = (event) => {
    event.preventDefault()
    const { email, username, password } = this.state
    alert(`Your registration detail: \n 
           Email: ${email} \n 
           Username: ${username} \n
           Password: ${password}`)
  }
  
  render() {    
    return (
      <React.Fragment>
      <h1>A Wizard Form!</h1>
      <p>Step {this.state.currentStep} </p> 
       
      <form onSubmit={this.handleSubmit}>
      {/* 
        render the form steps and pass required props in
      */}

        <Step1 
          currentStep={this.state.currentStep} 
          handleChange={this.handleChange}
          email={this.state.email}
        />
        <Step2 
          currentStep={this.state.currentStep} 
          handleChange={this.handleChange}
          username={this.state.username}
        />
        <Step3 
          currentStep={this.state.currentStep} 
          handleChange={this.handleChange}
          password={this.state.password}
        />       

      </form>
      </React.Fragment>
    )
  }
}

Then we add the next or previous step function, like this:

class MasterForm extends Component {
  constructor(props) {
    super(props)
    /*
    * bind new functions for next and previous form
    */
    this._next = this._next.bind(this)
    this._prev = this._prev.bind(this)
  }

  /*
  * Test current step with ternary
  */

  /*
  * _next and _previous functions will be called on button click
  */

  _next() {
    let currentStep = this.state.currentStep
    currentStep = currentStep >= 2? 3: currentStep + 1
    this.setState({
      currentStep: currentStep
    })
  }
    
  _prev() {
    let currentStep = this.state.currentStep
    currentStep = currentStep <= 1? 1: currentStep - 1
    this.setState({
      currentStep: currentStep
    })
  }

  /*
  * the functions for our button
  */
  get previousButton(){
    let currentStep = this.state.currentStep
    if(currentStep !==1){
      return (
        <button className="btn btn-secondary" type="button" onClick={this._prev}>
        Previous
        </button>
      )
    }
    return null
  }

  get nextButton(){
    let currentStep = this.state.currentStep
    if(currentStep <3){
      return (
        <button className="btn btn-primary float-right" type="button" onClick={this._next}>
        Next
        </button>        
      )
    }
    return null
  }

  /*
  * add buttons to our form in render
  */
  render(){
    return(
      <form onSubmit={this.handleSubmit}>
        {/* 
          ... other codes
        */}
       
        {this.previousButton}
        {this.nextButton}
       
      </form>
    )
  }
}

There you go! For a complete example with validation and submission from our previous example form, here is a codepen link.

Mastering React Form book

I have been developing React form extensively for a business application that required all the features I have written during the past few weeks. It turns out building forms with React is both hard and challenging. I'm currently writing a comprehensive guide to forms in React from beginner to advanced. Unlike my tutorials, the book will include a real form practice build with best practices and source code which will be interesting if you are building a form with React.


Get my weekly newsletter ✉️
Let's explore the exciting world of React together.
No Spam. Unsubscribe anytime