Stepper

The RhStepper component is a user interface element that displays a linear sequence of steps that a user can progress through.

Usage

Once installed, you can import the RhStepper component into your React application:

copy
import {RhStepper} from "@rhythm-ui/react"

Then, you can use the RhStepper component in your JSX code:

copy
function Default() {
  const [activeStep, setActiveStep] = useState(0);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper activeStep={activeStep} onClick={StepButtonHandler}>
        <RhStepperStep />
        <RhStepperStep />
        <RhStepperStep />
        <RhStepperStep />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Stepper Props

activeStepnumber0ActiveStep prop takes the value as an integer that denotes the step number for all the steps from 0 to the length of steps.
showCompletedActionIconbooleanfalseShowCompletedActionIcon prop takes the value as a boolean. If true, completed steps will show the completed icon based on the icon given by the user in the error icon or success icon prop.
layout"horizontal" | "vertical"horizontalLayout prop takes the value as 'horizontal' or 'vertical'
nonLinearbooleantrueNonLinear prop takes the value as a boolean. If true, then the user can not jump from one step to another without completing the current step.
autoNumberingbooleanAutoNumbering prop takes the value as a boolean. If true, it will show the steps count on each step.
circleSizestring30pxCircleSize prop takes the value as a string that should be the size of a circle with unit px.
stepConnectorDistancestring2emStepConnectorDistance prop takes values as string that accepts pixel value to make connector length small or large.
enableConnectorColorbooleantrueEnableConnectorColor prop takes the value as a boolean. If true, the connector color will be visible.
errorIconReact.ReactNode<RhIcon icon='heroicons:x-mark' />ErrorIcon takes the value as JSX. This will be shown after step completion with isError = true for <step /> component.
successIconReact.ReactNode<RhIcon icon='heroicons:check' />SuccessIcon takes the value as JSX. This will be shown after step completion with isError = false for <step /> component. By default isError is false for <step /> component.

Auto Numbering

The autoNumbering prop If set to true, step numbers will be automatically generated and displayed.

copy
function AutoNumbering() {
  const [activeStep, setActiveStep] = useState(0);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper
        autoNumbering
        circleSize="25px"
        enableConnectorColor={false}
        activeStep={activeStep}
        onClick={StepButtonHandler}
      >
        <RhStepperStep />
        <RhStepperStep />
        <RhStepperStep />
        <RhStepperStep />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Connector

If the enableConnectorColor prop is true then the connector between steps can have a different color than the default.

copy
function Connector() {
  const [activeStep, setActiveStep] = useState(0);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper
        autoNumbering
        circleSize="25px"
        enableConnectorColor={true}
        activeStep={activeStep}
        onClick={StepButtonHandler}
      >
        <RhStepperStep />
        <RhStepperStep />
        <RhStepperStep />
        <RhStepperStep />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Label

The label prop of RhStepperStep will display the label on each step.

Step1
Step2
Step3
Step4
copy
function Label() {
  const [activeStep, setActiveStep] = useState(0);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper
        autoNumbering
        circleSize="25px"
        enableConnectorColor={true}
        activeStep={activeStep}
        onClick={StepButtonHandler}
      >
        <RhStepperStep label="Step1" />
        <RhStepperStep label="Step2" />
        <RhStepperStep label="Step3" />
        <RhStepperStep label="Step4" />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Non Linear

The NonLinear prop takes the value as a boolean. If true, then the user can not jump from one step to another without completing the current step.

Step1
Step2
Step3
Step4
copy
function NonLinear() {
  const [activeStep, setActiveStep] = useState(0);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper
        autoNumbering
        nonLinear={false}
        circleSize="25px"
        enableConnectorColor={true}
        activeStep={activeStep}
        onClick={StepButtonHandler}
      >
        <RhStepperStep label="Step1" />
        <RhStepperStep label="Step2" />
        <RhStepperStep label="Step3" />
        <RhStepperStep label="Step4" />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Icon

An optional icon is to be displayed within the step.

Login Details
User Profile
Membership
Verification
copy
function Icon() {
  const [activeStep, setActiveStep] = useState(0);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper
        autoNumbering
        nonLinear={false}
        circleSize="25px"
        enableConnectorColor={true}
        activeStep={activeStep}
        onClick={StepButtonHandler}
      >
        <RhStepperStep
          icon={<RhIcon icon="clarity:login-line" />}
          label="Login Details"
        />
        <RhStepperStep icon={<RhIcon icon="bx:user" />} label="User Profile" />
        <RhStepperStep
          icon={<RhIcon icon="clarity:group-solid" />}
          label="Membership"
        />
        <RhStepperStep
          icon={<RhIcon icon="uiw:verification" />}
          label="Verification"
        />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Success Icon

The successIcon and showCompletedActionIcon props will display an icon on a completed step icon.

Login Details
User Profile
Membership
Verification
copy
function Success() {
  const [activeStep, setActiveStep] = useState(0);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper
        autoNumbering
        showCompletedActionIcon
        successIcon={<RhIcon className="text-lg" icon="heroicons:check" />}
        circleSize="25px"
        enableConnectorColor={true}
        activeStep={activeStep}
        onClick={StepButtonHandler}
      >
        <RhStepperStep
          icon={<RhIcon icon="clarity:login-line" />}
          label="Login Details"
        />
        <RhStepperStep icon={<RhIcon icon="bx:user" />} label="User Profile" />
        <RhStepperStep
          icon={<RhIcon icon="clarity:group-solid" />}
          label="Membership"
        />
        <RhStepperStep
          icon={<RhIcon icon="uiw:verification" />}
          label="Verification"
        />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Failure

The errorIcon is displayed on a step with an error.

Login Details
User Profile
Membership
Verification
copy
function Failure() {
  const [activeStep, setActiveStep] = useState(4);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper
        activeStep={4}
        autoNumbering
        circleSize="35px"
        errorIcon={<RhIcon className="text-lg" icon="heroicons:x-mark" />}
        onClick={() => {}}
        showCompletedActionIcon
        successIcon={<RhIcon className="text-lg" icon="heroicons:check" />}
      >
        <RhStepperStep
          icon={<RhIcon icon="clarity:login-line" />}
          label="Login Details"
        />
        <RhStepperStep icon={<RhIcon icon="bx:user" />} label="User Profile" />
        <RhStepperStep
          icon={<RhIcon icon="clarity:group-solid" />}
          isError
          label="Membership"
        />
        <RhStepperStep
          icon={<RhIcon icon="uiw:verification" />}
          label="Verification"
        />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Disable

The isDisable prop provides flexibility to disable or enable certain steps in the sequence based on user interactions or system conditions.

Login Details
User Profile
Membership
Verification
copy
function Disable() {
  const [activeStep, setActiveStep] = useState(0);
  const STEP_SIZE = 4;

  const incrementStep = () => {
    setActiveStep((prev) => {
      if (prev <= STEP_SIZE - 1) {
        return prev + 1;
      } else return prev;
    });
  };

  const decrementStep = () => {
    setActiveStep((prev) => {
      if (prev > 0) {
        return prev - 1;
      } else return prev;
    });
  };

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <RhStepper
        activeStep={4}
        autoNumbering
        circleSize="35px"
        errorIcon={<RhIcon className="text-lg" icon="heroicons:x-mark" />}
        onClick={() => {}}
        showCompletedActionIcon
        successIcon={<RhIcon className="text-lg" icon="heroicons:check" />}
      >
        <RhStepperStep
          icon={<RhIcon icon="clarity:login-line" />}
          label="Login Details"
        />
        <RhStepperStep
          icon={<RhIcon icon="bx:user" />}
          isDisable
          label="User Profile"
        />
        <RhStepperStep
          icon={<RhIcon icon="clarity:group-solid" />}
          isDisable
          label="Membership"
        />
        <RhStepperStep
          icon={<RhIcon icon="uiw:verification" />}
          label="Verification"
        />
      </RhStepper>
      <div className="flex justify-evenly">
        <RhButton onClick={decrementStep}>Previous</RhButton>
        <RhButton onClick={incrementStep}>Next</RhButton>
      </div>
    </div>
  );
}

Vertical

The layout prop of the stepper can be either horizontal or vertical.

Login Details
User Profile
Membership
Verification
copy
function verticalTemplate() {
  const [activeStep, setActiveStep] = useState(0);
  const [status, setStatus] = useState("");

  const StepButtonHandler = (id) => {
    setActiveStep(id);
  };

  return (
    <div className="flex flex-col gap-8">
      <div className="h-screen">
        <RhStepper
          activeStep={activeStep}
          onClick={StepButtonHandler}
          status={status}
          layout="vertical"
        >
          <RhStepperStep
            label="Login Details"
            icon={<RhIcon icon="clarity:login-line" />}
          />
          <RhStepperStep
            label="User Profile"
            icon={<RhIcon icon="bx:user" />}
          />
          <RhStepperStep
            label="Membership"
            icon={<RhIcon icon="clarity:group-solid" />}
          />
          <RhStepperStep
            label="Verification"
            icon={<RhIcon icon="uiw:verification" />}
          />
        </RhStepper>
      </div>
    </div>
  );
}