JavaScript/jQuery Stepper - Getting Started

jQuery
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
Angular
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
Vue
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
React
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.

Stepper is a UI component that allows users to navigate multi-step forms and processes such as checkouts, set-up wizards, and sign-up forms.

This tutorial guides you through the following steps:

  • Add a Stepper to a page.
  • Configure the component's core settings.
  • Create six steps and specify their icons, labels, and other options.
  • Disable completed steps as a user progresses.

Each section in this tutorial covers a single configuration step. You can find the complete source code in the following GitHub repository:

View on GitHub

Create Stepper

jQuery

Add DevExtreme to your jQuery application and use the code below to create a Stepper component with one step. This example utilizes the items[] array, but you can use dataSource to define steps as well. If you do not specify at least one step, Stepper does not display any data.

index.js
index.html
$(function() {
    $("#stepper").dxStepper({
        items: [
            {}
        ]
    });
});
<html>
    <head>
        <!-- ... -->
        <script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
        <link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/25.1.3/css/dx.light.css">
        <script type="text/javascript" src="https://cdn3.devexpress.com/jslib/25.1.3/js/dx.all.js"></script>
        <script type="text/javascript" src="index.js"></script>
    </head>
    <body>
        <div id="stepper"></div>
    </body>
</html>
Angular

Add DevExtreme to your Angular application and use the code below to create a Stepper component with one step. This example utilizes the items[] array, but you can use dataSource to define steps as well. If you do not specify at least one step, Stepper will display no data.

app.component.html
app.component.ts
app.module.ts
<dx-stepper
    [items]="steps">
</dx-stepper>
import { Component } from '@angular/core';
import { DxStepperTypes } from 'devextreme-angular/ui/stepper';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    steps: DxStepperTypes.Item[] = [
        {}
    ]
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DxStepperModule } from 'devextreme-angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        AppRoutingModule,
        DxStepperModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }
Vue

Add DevExtreme to your Vue application and use the code below to create a Stepper component with one step. This example utilizes the items[] array, but you can use dataSource to define steps as well. If you do not specify at least one step, Stepper will display no data.

App.vue
<script setup lang="ts">
    import { reactive } from 'vue';
    import { DxStepper, DxItem, DxStepperTypes } from 'devextreme-vue/stepper';
    import 'devextreme/dist/css/dx.light.css';

    const items: DxStepperTypes.Item[] = reactive([
        {}
    ]);
</script>
<template>
    <DxStepper ref="stepperRef">
        <DxItem
            v-for="(item, index) in items"
            :key="index"
        />
    </DxStepper>
</template>
React

Add DevExtreme to your React application and use the code below to create a Stepper component with one step. This example utilizes the items[] array, but you can use dataSource to define steps as well. If you do not specify at least one step, Stepper will display no data.

App.tsx
import React, { JSX, useState } from 'react';
import { Stepper, Item, StepperTypes } from 'devextreme-react/stepper';
import 'devextreme/dist/css/dx.light.css';

export default function App(): JSX.Element {
    const [steps, setSteps] = useState<any[]>([
        {}
    ]);

    return (
        <Stepper>
            {
                steps.map((item, index) => (
                    <Item key={index} {...item} />
                ))
            }
        </Stepper>
    );
}

Configure Steps

This tutorial specifies the following properties to configure Stepper steps:

  • label
    Assigns a step label.
  • icon
    Defines a step icon. For more information on the DevExteme icon library, refer to DevExtreme Icons.
  • optional
    Adds an "Optional" caption.

You can also specify the following properties:

  • isValid
    Indicates input validation state for a step.
  • text
    Defines text displayed on a step indicator.
  • hint
    Specifies step hint text that appears upon hover or long-press.
  • disabled
    Disables a step.

The code below assigns six steps to the component with different configuration options.

jQuery
index.js
$(function() {
    $("#stepper").dxStepper({
        items: [{
            label: 'Personal Details'
        }, {
            label: 'Program Selection',
            icon: 'detailslayout'
        }, {
            label: 'Campus and Start Dates',
            icon: 'map'
        }, {
            label: 'Supporting Documents',
            icon: 'textdocument'
        }, {
            label: 'Scholarship and Aid',
            icon: 'money',
            optional: true
        }, {
            label: 'Review and Submit',
            icon: 'send'
        }]
    });
});
Angular
app.component.ts
// ...

@Component({
    // ...
})
export class AppComponent {
    steps: DxStepperTypes.Item[] = [
        { label: 'Personal Details' },
        { label: 'Program Selection', icon: 'detailslayout' },
        { label: 'Campus and Start Dates', icon: 'map' },
        { label: 'Supporting Documents', icon: 'textdocument' },
        { label: 'Scholarship and Aid', icon: 'money', optional: true },
        { label: 'Review and Submit', icon: 'send' }
    ];
}
Vue
App.vue
<script setup lang="ts">
    // ...
    const items: DxStepperTypes.Item[] = reactive([
        { label: 'Personal Details' },
        { label: 'Program Selection', icon: 'detailslayout' },
        { label: 'Campus and Start Dates', icon: 'map' },
        { label: 'Supporting Documents', icon: 'textdocument' },
        { label: 'Scholarship and Aid', icon: 'money', optional: true },
        { label: 'Review and Submit', icon: 'send' }
    ]);
</script>
<template>
    // ...
</template>
React
App.tsx
// ...

export default function App(): JSX.Element {
    const [steps, setSteps] = useState<any[]>([
        { label: 'Personal Details' },
        { label: 'Program Selection', icon: 'detailslayout' },
        { label: 'Campus and Start Dates', icon: 'map' },
        { label: 'Supporting Documents', icon: 'textdocument' },
        { label: 'Scholarship and Aid', icon: 'money', optional: true },
        { label: 'Review and Submit', icon: 'send' },
    ]);

    return (
        // ...
    );
}

Customize Steps

Specify items[].template to customize an individual step, or itemTemplate to customize all steps in the component. This tutorial replaces the first step indicator with a star.

For instructions on how to define item templates, refer to Object Structures - template.

jQuery
index.js
index.css
$(function() {
    $("#stepper").dxStepper({
        items: [{
            label: 'Personal Details',
            template: (data) => `
                <div class='star dx-step-indicator'></div>
                <div class='dx-step-caption'>
                    <div class='dx-step-label'>${data.label}</div>
                </div>
            `,
        }, 
        // ...
        ]
    });
});
.star { 
    aspect-ratio: 1;
    clip-path: polygon(50% 0,79% 90%,2% 35%,98% 35%,21% 90%); 
    box-shadow: 0 0 0 8px #fafafa;
}
Angular
app.component.html
app.component.ts
app.component.scss
<dx-stepper
    [items]="steps"
>
    <div *dxTemplate="let data of 'starTemplate'">
        <div class="star dx-step-indicator"></div>
        <div class="dx-step-caption">
            <div class="dx-step-label">{{ data.label }}</div>
        </div>
    </div>
</dx-stepper>
// ...

@Component({
    // ...
})
export class AppComponent {
    steps: DxStepperTypes.Item[] = [
        { label: 'Personal Details', template: 'starTemplate' },
        // ...
    ];
}
.star { 
    aspect-ratio: 1;
    clip-path: polygon(50% 0,79% 90%,2% 35%,98% 35%,21% 90%); 
    box-shadow: 0 0 0 8px #fafafa;
}
Vue
App.vue
<script setup lang="ts">
    // ...
    const items: DxStepperTypes.Item[] = reactive([
        { label: 'Personal Details', template: 'star' },
        // ...
    ]);
</script>
<template>
    <DxStepper ref="stepperRef" @selection-changed="onSelectionChanged">
        <DxItem
            v-for="(item, index) in items"
            :key="index"
            v-bind="item"
            :template="item.template"
        />
        <template #star="{ data }">
            <div class="star dx-step-indicator"></div>
            <div class="dx-step-caption">
                <div class="dx-step-label">{{ data.label }}</div>
            </div>
        </template>
    </DxStepper>
</template>
<style>
    .star { 
        aspect-ratio: 1;
        clip-path: polygon(50% 0,79% 90%,2% 35%,98% 35%,21% 90%); 
        box-shadow: 0 0 0 8px #fafafa;
    }
</style>
React
App.tsx
index.css
// ...

export default function App(): JSX.Element {
    function renderStarTemplate(data: StepperTypes.Item): JSX.Element {
        return (
            <React.Fragment>
                <div className="star dx-step-indicator"></div>
                <div className="dx-step-caption">
                <div className="dx-step-label">{data.label}</div>
                </div>
            </React.Fragment>
        );
    }

    const [steps, setSteps] = useState<any[]>([
        { label: 'Personal Details', render: renderStarTemplate },
        // ...
    ]);

    return (
        // ...
    );
}
.star { 
    aspect-ratio: 1;
    clip-path: polygon(50% 0,79% 90%,2% 35%,98% 35%,21% 90%); 
    box-shadow: 0 0 0 8px #fafafa;
}

Configure Selection

The following Stepper properties allow you to configure selection at runtime:

This tutorial uses onSelectionChanged to disable steps as users move through the component.

jQuery
index.js
$(function() {
    $("#stepper").dxStepper({
        // ...
        onSelectionChanged: ({ component, addedItems }) => {
            const items = component.option("items");
            const newIndex = items.findIndex(item => addedItems[0].label === item.label);
            component.option(`items[${newIndex - 1}].disabled`, true);
        },
    });
});
Angular
app.component.html
app.component.ts
<dx-stepper 
    [items]="steps"
    (onSelectionChanged)="onSelectionChanged($event)">
    // ...
</dx-stepper>
// ...

@Component({
    // ...
})
export class AppComponent {
    onSelectionChanged(e: DxStepperTypes.SelectionChangedEvent): void {
        const newItem = e.addedItems[0];
        const newIndex = this.steps.findIndex(item => item.label === newItem.label);

        if (newIndex > 0 && !this.steps[newIndex - 1].disabled) {
            this.steps[newIndex - 1].disabled = true;
        }
    }
}
Vue
App.vue
<script setup lang="ts">
    // ...
    const onSelectionChanged = (e: DxStepperTypes.SelectionChangedEvent) => {
        const newItem = e.addedItems[0];
        const newIndex = items.findIndex((item) => item.label === newItem.label);
        if (newIndex > 0) {
            items[newIndex - 1].disabled = true;
        }
    };
</script>
<template>
    <DxStepper ref="stepperRef" @selection-changed="onSelectionChanged">
        <!-- ... -->
    </DxStepper>
</template>
React
App.tsx
// ...

export default function App(): JSX.Element {
    // ...

    function onSelectionChanged(e: StepperTypes.SelectionChangedEvent): void {
        const newItem = e.addedItems[0];
        const newIndex = steps.findIndex((item) => item.label === newItem.label);

        if (newIndex > 0 && !steps[newIndex - 1].disabled) {
            const updated = [...steps];
            updated[newIndex - 1] = { ...updated[newIndex - 1], disabled: true };
            setSteps(updated);
        }
    }

    return (
        <Stepper onSelectionChanged={onSelectionChanged}>
            // ...
        </Stepper>
    );
}