In enterprise environments, users rarely think in isolated actions.

They think in processes.

Every business operation usually follows a sequence:

a training process follows certification milestones

a document moves through approval stages

a ticket evolves through support statuses

an onboarding request passes multiple departments

a deployment pipeline moves through environments


Building Step-Based User Interfaces in SPFx with ProgressStepsIndicator

In enterprise environments, users rarely think in isolated actions.

They think in processes.

Every business operation usually follows a sequence:

  • a document moves through approval stages
  • a ticket evolves through support statuses
  • an onboarding request passes multiple departments
  • a deployment pipeline moves through environments
  • a training process follows certification milestones

This means one thing:

state visualization matters

One of the biggest UX mistakes in corporate applications is showing process state as plain text.

Example:

Status: In Review

Technically correct.

But visually weak.

Users immediately understand much more when status is represented as a journey.

Example:

Draft → Review → Approved → Published

This creates context.

Context reduces confusion.

Confusion reduction improves productivity.

That is exactly where ProgressStepsIndicator becomes useful.

Instead of building custom progress bars, step components, or status pipelines manually, the PnP SPFx React Controls library gives us a reusable and production-ready control.

This control is especially powerful inside the SharePoint Framework ecosystem because it integrates naturally with:

  • SharePoint Lists
  • Power Automate
  • Fluent UI
  • Microsoft Teams
  • tenant themes
  • dark mode

This makes it ideal for building modern process-driven SharePoint solutions.

In this article we will explore:

  • what the control does
  • how it works
  • how to integrate it into SPFx
  • how to make it theme-aware
  • how it connects to Fluent UI concepts
  • enterprise use cases
  • best practices

We will build a complete example.


What is ProgressStepsIndicator?

The ProgressStepsIndicator is a PnP reusable SPFx control that renders a multi-step visual process.

Example:

Planning → Development → Testing → Deployment

Instead of this:

Current Status: Testing

you get:

✓ Planning
✓ Development
➜ Testing
○ Deployment

This makes workflow state much easier to understand.

Official documentation:

PnP ProgressStepsIndicator Documentation


Why use it?

Because enterprise users think in stages.

Examples:


Document approval

Draft
Review
Approved
Published
Archived

Employee onboarding

Registration
HR Validation
IT Provisioning
Training
Completed

Incident management

Opened
Assigned
In Progress
Resolved
Closed

SharePoint migration

Discovery
Planning
Migration
Validation
Go Live

Perfect for SharePoint.


Architecture of our solution

We will use:

SPFx Web Part
ThemeProvider
React Component
ProgressStepsIndicator

This follows the correct SPFx architecture.


Why ThemeProvider matters here

The control accepts:

themeVariant

This makes it inherit:

  • SharePoint site theme
  • tenant branding
  • dark mode
  • Teams theme

Without this:

visual inconsistency

With this:

native Microsoft 365 experience

Project Structure

src/
├── webparts/
│ ├── progressStepsIndicatorWp/
│ │ ├── ProgressStepsIndicatorWpWebPart.ts
│ │ ├── components/
│ │ │ ├── IProgressStepsIndicatorWpProps.ts
│ │ │ ├── ProgressStepsIndicatorWp.tsx

Simple.

Clean.

Scalable.


Step 1 — Create the props interface

File:

IProgressStepsIndicatorWpProps.ts

import { IReadonlyTheme } from '@microsoft/sp-component-base';
export interface IProgressStepsIndicatorWpProps {
themeVariant?: IReadonlyTheme;
}

Why optional?

Because during initialization:

theme may not yet be loaded

This avoids runtime issues.


Step 2 — Web Part setup

File:

ProgressStepsIndicatorWpWebPart.ts

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import {
ThemeProvider,
ThemeChangedEventArgs,
IReadonlyTheme
} from '@microsoft/sp-component-base';
import * as strings from 'ProgressStepsIndicatorWpWebPartStrings';
import ProgressStepsIndicatorWp from './components/ProgressStepsIndicatorWp';
import { IProgressStepsIndicatorWpProps } from './components/IProgressStepsIndicatorWpProps';
export interface IProgressStepsIndicatorWpWebPartProps {
description: string;
}
export default class ProgressStepsIndicatorWpWebPart extends BaseClientSideWebPart<IProgressStepsIndicatorWpWebPartProps> {
private _themeProvider: ThemeProvider | undefined;
private _themeVariant: IReadonlyTheme | undefined;
public render(): void {
const element: React.ReactElement<IProgressStepsIndicatorWpProps> =
React.createElement(
ProgressStepsIndicatorWp,
{
themeVariant: this._themeVariant
}
);
ReactDom.render(element, this.domElement);
}
protected onInit(): Promise<void> {
this._themeProvider = this.context.serviceScope.consume(
ThemeProvider.serviceKey
);
this._themeVariant = this._themeProvider.tryGetTheme();
this._themeProvider.themeChangedEvent.add(
this,
this._handleThemeChangedEvent
);
return Promise.resolve();
}
private _handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
this._themeVariant = args.theme;
this.render();
}
protected onDispose(): void {
if (this._themeProvider) {
this._themeProvider.themeChangedEvent.remove(
this,
this._handleThemeChangedEvent
);
}
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
}

Important concept: Why not this.context.themeProvider?

Wrong:

this.context.themeProvider.tryGetTheme()

This causes:

Property 'themeProvider' does not exist

Correct:

this.context.serviceScope.consume(ThemeProvider.serviceKey)

This is the SPFx dependency injection model.

Very important.


Step 3 — React component

File:

ProgressStepsIndicatorWp.tsx

import * as React from 'react';
import { IProgressStepsIndicatorWpProps } from './IProgressStepsIndicatorWpProps';
import { ProgressStepsIndicator } from '@pnp/spfx-controls-react/lib/ProgressStepsIndicator';
const progressSteps = [
{
title: 'Planning',
description: 'Requirements and analysis'
},
{
title: 'Development',
description: 'Building the solution'
},
{
title: 'Testing',
description: 'Validation and QA'
},
{
title: 'Deployment',
description: 'Production release'
}
];
const ProgressStepsIndicatorWp: React.FC<IProgressStepsIndicatorWpProps> = ({
themeVariant
}) => {
return (
<ProgressStepsIndicator
steps={progressSteps}
currentStep={2}
themeVariant={themeVariant}
/>
);
};
export default ProgressStepsIndicatorWp;

Understanding currentStep

This property controls current active stage.

Example:

currentStep={0}

Output:

➜ Planning
○ Development
○ Testing
○ Deployment

currentStep={2}

Output:

✓ Planning
✓ Development
➜ Testing
○ Deployment

Very useful for workflow states.


Relation to Fluent UI

PnP controls are built on top of Fluent UI.

Meaning:

this control inherits:

  • Microsoft styling system
  • accessibility standards
  • responsive behavior
  • keyboard navigation
  • theme tokens

Fluent UI documentation:

Microsoft Fluent UI Documentation

This is why the control feels native.


SPFx relation

This control runs inside:

SharePoint Framework

SPFx provides:

  • context
  • lifecycle
  • dependency injection
  • theme services
  • property pane
  • Microsoft 365 host integration

Official SPFx docs:

Microsoft Learn – SharePoint Framework Overview


Final thoughts

ProgressStepsIndicator is not just a visual component.

It is a process communication component.

That distinction matters.

In enterprise systems, process clarity is often more important than raw data.

When users understand where they are in a process:

  • they make fewer mistakes
  • they require less training
  • they complete tasks faster
  • they trust the system more

That is the true value of good UX.

And this control solves that elegantly inside the Microsoft 365 ecosystem.

Edvaldo Guimrães Filho Avatar

Published by