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.

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.

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.

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.

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.

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.

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.

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>
  );
}