Building a Enum Based Form Stepper In React Typescript

January 18, 2021

We look at a streamlined form stepper design pattern.

First, we are going to create our steps as enums. If we don't define values for our enums the will by default increment. This design pattern works best with at least 3 form steps.

enum FormState {
  Default, // 0
  Address, // 1
  Billing, // 2
}

Next, let's add the state and for now, we will deal with only the default case which is the first step in our multipart from stepper. We can see that we can now also increment the stepper, because of our default case it will also be our first step named default.

enum FormState {
  Default, // 0
  Address, // 1
  Billing, // 2
}

const Form: React.FC = () => {
  const [formStep, setFormStep] = useState<FormState>(FormState.Default);
  let step;
  switch (formStep) {
    case FormState.Default:
    default:
      step = <>First step</>;
      break;
  }
  return (
    <form>
      <div>{step}</div>
      <div>
        <Button onClick={() => setFormStep(formStep - 1)}>Back</Button>
        <Button onClick={() => setFormStep(formStep + 1)}>Next</Button>
      </div>
    </form>
  );
};

Lastly, we add the other steps (which could be components defined elsewhere) and

gridTemplateAreas
, which we could use to further change our form layout.

enum FormState {
  Default, // 0
  Address, // 1
  Billing, // 2
}

const Form = () => {
  const [formStep, setFormStep] = useState<FormState>(FormState.Default);
  let step;
  switch (formStep) {
    case FormState.Address:
      step = <div>Address Step</div>;
      break;
    case FormState.Billing:
      step = <div>Billing Step</div>;
      break;
    case FormState.Default:
    default:
      step = <>First step</>;
      break;
  }
  return (
    <form
      style={{
        gridTemplateAreas: "'step-body' 'step-action'",
        gridTemplateRows: "auto auto",
      }}
    >
      <div style={{ gridArea: "step-body" }}>{step}</div>
      <div className="flex justify-end" style={{ gridArea: "step-action" }}>
        <Button onClick={() => setFormStep(formStep - 1)} className="mr-2">
          Back
        </Button>
        <Button onClick={() => setFormStep(formStep + 1)}>Next</Button>
      </div>
    </form>
  );
};