Meet Frontfriend.Your brand. Your rules. Your agents, connected.Explore what’s new
DocumentationStepper

Stepper

A step-by-step process for users to navigate through a series of steps.

Interactive preview

Rendered from the shared Frontfriend stories with React Base, the same preview Studio shows. Styling comes from your Design System; the code below is native to Reka UI.

Installation

Complete project setup first. Your configuration must select vue/reka and include your Design System ID.

pnpm exec frontfriend download stepper -f vue --primitive reka

Review dependencies reported by the downloader and keep your source changes in version control.

Usage

<script setup lang="ts">
import { Stepper } from "@/components/ui/stepper";
import { StepperNav } from "@/components/ui/stepper";
import { StepperItem } from "@/components/ui/stepper";
import { StepperTrigger } from "@/components/ui/stepper";
import { StepperIndicator } from "@/components/ui/stepper";
import { StepperSeparator } from "@/components/ui/stepper";
import { StepperPanel } from "@/components/ui/stepper";
import { StepperContent } from "@/components/ui/stepper";
import { reactive } from "vue";
import * as UI from "@/components/ui/stepper";

const Component = UI.Stepper;

const args = reactive(
  {
    "defaultValue": 2,
    "orientation": "horizontal",
    "value": 2,
  } satisfies Partial<
    InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
  > &
    Record<string, unknown> & { orientation?: "horizontal" | "vertical" },
);
</script>

<template>
  <Stepper v-model="args.value" :orientation="args.orientation"
    ><StepperNav
      ><StepperItem v-for="step in 3" :key="step" :step="step"
        ><StepperTrigger
          ><StepperIndicator>{{ step }}</StepperIndicator></StepperTrigger
        ><StepperSeparator v-if="step < 3" /></StepperItem></StepperNav
    ><StepperPanel
      ><StepperContent v-for="step in 3" :key="step" :value="step"
        >Step {{ step }}</StepperContent
      ></StepperPanel
    ></Stepper
  >
</template>

Examples

Default

<script setup lang="ts">
import { Stepper } from "@/components/ui/stepper";
import { StepperNav } from "@/components/ui/stepper";
import { StepperItem } from "@/components/ui/stepper";
import { StepperTrigger } from "@/components/ui/stepper";
import { StepperIndicator } from "@/components/ui/stepper";
import { StepperSeparator } from "@/components/ui/stepper";
import { StepperPanel } from "@/components/ui/stepper";
import { StepperContent } from "@/components/ui/stepper";
import * as UI from "@/components/ui/stepper";

const steps = [1, 2, 3, 4];

const Component = UI.Stepper;

const args = {
  "defaultValue": 2,
  "orientation": "horizontal",
} satisfies Partial<
  InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown> & { orientation?: "horizontal" | "vertical" };
</script>

<template>
  <Stepper :default-value="2" class="w-[420px] space-y-8">
    <StepperNav>
      <StepperItem v-for="step in steps" :key="step" :step="step">
        <StepperTrigger>
          <StepperIndicator>{{ step }}</StepperIndicator>
        </StepperTrigger>
        <StepperSeparator v-if="steps.length > step" />
      </StepperItem>
    </StepperNav>
    <StepperPanel class="text-sm">
      <StepperContent
        v-for="step in steps"
        :key="step"
        :value="step"
        class="flex items-center justify-center"
      >
        Step {{ step }} content
      </StepperContent>
    </StepperPanel>
  </Stepper>
</template>

States

<script setup lang="ts">
import { Stepper } from "@/components/ui/stepper";
import { StepperNav } from "@/components/ui/stepper";
import { StepperItem } from "@/components/ui/stepper";
import { StepperTrigger } from "@/components/ui/stepper";
import { StepperIndicator } from "@/components/ui/stepper";
import { StepperSeparator } from "@/components/ui/stepper";
import * as UI from "@/components/ui/stepper";

const Component = UI.Stepper;

const args = {
  "defaultValue": 2,
  "orientation": "horizontal",
} satisfies Partial<
  InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown> & { orientation?: "horizontal" | "vertical" };
</script>

<template>
  <Stepper :default-value="2" class="w-[420px] space-y-8">
    <StepperNav>
      <StepperItem :step="1" completed>
        <StepperTrigger><StepperIndicator>1</StepperIndicator></StepperTrigger>
        <StepperSeparator />
      </StepperItem>
      <StepperItem :step="2">
        <StepperTrigger><StepperIndicator>2</StepperIndicator></StepperTrigger>
        <StepperSeparator />
      </StepperItem>
      <StepperItem :step="3" loading>
        <StepperTrigger><StepperIndicator>3</StepperIndicator></StepperTrigger>
        <StepperSeparator />
      </StepperItem>
      <StepperItem :step="4" disabled>
        <StepperTrigger><StepperIndicator>4</StepperIndicator></StepperTrigger>
      </StepperItem>
    </StepperNav>
  </Stepper>
</template>

Controlled

<script setup lang="ts">
import { ref } from "vue";
import { Stepper } from "@/components/ui/stepper";
import { StepperNav } from "@/components/ui/stepper";
import { StepperItem } from "@/components/ui/stepper";
import { StepperTrigger } from "@/components/ui/stepper";
import { StepperIndicator } from "@/components/ui/stepper";
import { StepperSeparator } from "@/components/ui/stepper";
import { StepperPanel } from "@/components/ui/stepper";
import { StepperContent } from "@/components/ui/stepper";
import { Button } from "@/components/ui/button";
import * as UI from "@/components/ui/stepper";

const steps = [1, 2, 3, 4];

const Component = UI.Stepper;

const args = {
  "defaultValue": 2,
  "orientation": "horizontal",
} satisfies Partial<
  InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown> & { orientation?: "horizontal" | "vertical" };
const currentStep = ref(2);
const prevStep = () => {
  currentStep.value = Math.max(1, currentStep.value - 1);
};
const nextStep = () => {
  currentStep.value = Math.min(steps.length, currentStep.value + 1);
};
</script>

<template>
  <Stepper v-model="currentStep" class="w-[420px] space-y-8">
    <StepperNav>
      <StepperItem v-for="step in steps" :key="step" :step="step">
        <StepperTrigger
          ><StepperIndicator>{{ step }}</StepperIndicator></StepperTrigger
        >
        <StepperSeparator v-if="steps.length > step" />
      </StepperItem>
    </StepperNav>
    <StepperPanel class="text-sm">
      <StepperContent
        v-for="step in steps"
        :key="step"
        :value="step"
        class="flex items-center justify-center"
      >
        Step {{ step }} content
      </StepperContent>
    </StepperPanel>
    <div class="flex items-center justify-between gap-2.5">
      <Button
        variant="outline"
        size="sm"
        :disabled="currentStep === 1"
        @click="prevStep"
        >Previous</Button
      >
      <Button
        variant="outline"
        size="sm"
        :disabled="currentStep === steps.length"
        @click="nextStep"
        >Next</Button
      >
    </div>
  </Stepper>
</template>

Title & Description

<script setup lang="ts">
import { Stepper } from "@/components/ui/stepper";
import { StepperNav } from "@/components/ui/stepper";
import { StepperItem } from "@/components/ui/stepper";
import { StepperTrigger } from "@/components/ui/stepper";
import { StepperIndicator } from "@/components/ui/stepper";
import { StepperTitle } from "@/components/ui/stepper";
import { StepperDescription } from "@/components/ui/stepper";
import { StepperSeparator } from "@/components/ui/stepper";
import * as UI from "@/components/ui/stepper";

const titledSteps = [
  { title: "User Details", description: "Create your account" },
  { title: "Payment Info", description: "Add billing information" },
  { title: "Auth OTP", description: "Verify your identity" },
  { title: "Preview Form", description: "Review and finish" },
];

const Component = UI.Stepper;

const args = {
  "defaultValue": 2,
  "orientation": "horizontal",
} satisfies Partial<
  InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown> & { orientation?: "horizontal" | "vertical" };
</script>

<template>
  <Stepper :default-value="2" class="w-[560px] space-y-8">
    <StepperNav>
      <StepperItem
        v-for="(step, index) in titledSteps"
        :key="step.title"
        :step="index + 1"
        class="relative flex-1 items-start"
      >
        <StepperTrigger class="flex flex-col gap-2.5">
          <StepperIndicator>{{ index + 1 }}</StepperIndicator>
          <StepperTitle>{{ step.title }}</StepperTitle>
          <StepperDescription>{{ step.description }}</StepperDescription>
        </StepperTrigger>
        <StepperSeparator
          v-if="titledSteps.length > index + 1"
          class="absolute left-[calc(50%+0.875rem)] right-[calc(-50%+0.875rem)] top-3 m-0 flex-none"
        />
      </StepperItem>
    </StepperNav>
  </Stepper>
</template>

Vertical

<script setup lang="ts">
import { Stepper } from "@/components/ui/stepper";
import { StepperNav } from "@/components/ui/stepper";
import { StepperItem } from "@/components/ui/stepper";
import { StepperTrigger } from "@/components/ui/stepper";
import { StepperIndicator } from "@/components/ui/stepper";
import { StepperTitle } from "@/components/ui/stepper";
import { StepperDescription } from "@/components/ui/stepper";
import { StepperSeparator } from "@/components/ui/stepper";
import * as UI from "@/components/ui/stepper";

const titledSteps = [
  { title: "User Details", description: "Create your account" },
  { title: "Payment Info", description: "Add billing information" },
  { title: "Auth OTP", description: "Verify your identity" },
  { title: "Preview Form", description: "Review and finish" },
];

const Component = UI.Stepper;

const args = {
  "defaultValue": 2,
  "orientation": "horizontal",
} satisfies Partial<
  InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown> & { orientation?: "horizontal" | "vertical" };
</script>

<template>
  <Stepper
    :default-value="2"
    orientation="vertical"
    class="w-[420px] space-y-8"
  >
    <StepperNav>
      <StepperItem
        v-for="(step, index) in titledSteps"
        :key="step.title"
        :step="index + 1"
        class="relative items-start not-last:flex-1"
      >
        <StepperTrigger class="items-start gap-2.5 pb-12 last:pb-0">
          <StepperIndicator>{{ index + 1 }}</StepperIndicator>
          <div class="mt-0.5 grid gap-1 text-left">
            <StepperTitle>{{ step.title }}</StepperTitle>
            <StepperDescription>{{ step.description }}</StepperDescription>
          </div>
        </StepperTrigger>
        <StepperSeparator
          v-if="titledSteps.length > index + 1"
          class="absolute inset-y-0 left-3 top-7 -order-1 m-0 -translate-x-1/2 group-data-[orientation=vertical]/stepper-nav:h-[calc(100%-2rem)]"
        />
      </StepperItem>
    </StepperNav>
  </Stepper>
</template>

API reference

Frontfriend-owned props. Native HTML and inherited primitive props remain documented by Reka UI.

Stepper

Prop Type Default Description
class HTMLAttributes["class"] —
defaultValue number —
modelValue number —
orientation StepperOrientation —

StepperContent

Prop Type Default Description
class HTMLAttributes["class"] —
forceMount boolean —
value number —

StepperDescription

Prop Type Default Description
class HTMLAttributes["class"] —

StepperIndicator

Prop Type Default Description
class HTMLAttributes["class"] —

StepperItem

Prop Type Default Description
class HTMLAttributes["class"] —
completed boolean —
disabled boolean —
loading boolean —
step number —

StepperNav

Prop Type Default Description
class HTMLAttributes["class"] —

StepperPanel

Prop Type Default Description
class HTMLAttributes["class"] —

StepperSeparator

Prop Type Default Description
class HTMLAttributes["class"] —

StepperTitle

Prop Type Default Description
class HTMLAttributes["class"] —

StepperTrigger

Prop Type Default Description
asChild boolean —
class HTMLAttributes["class"] —

Configuration determines the actual classes and available styling hooks. See Theming & component configuration before introducing a repeated local override.