Mantine Select Stepper

Undolog

@gfazioli/mantine-select-stepper

Mantine SelectStepper is a React component that allows users to navigate through a list of options using increment and decrement buttons, providing an intuitive alternative to traditional dropdown selects for cycling through predefined values.

Installation

yarn add @gfazioli/mantine-select-stepper

After installation import package styles at the root of your application:

import '@gfazioli/mantine-select-stepper/styles.css';

You can import styles within a layer @layer mantine-select-stepper by importing @gfazioli/mantine-select-stepper/styles.layer.css file.

import '@gfazioli/mantine-select-stepper/styles.layer.css';

Usage

This is a description

React

Angular

Vue

Svelte

Ember

Animation duration
View width
Radius
import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo(props: any) {
  return <SelectStepper label="Select framework" description="This is a description"
    data={['React', 'Angular', 'Vue', 'Svelte', 'Ember']} />;
}

Controlled

The SelectStepper component can be fully controlled through the value prop. Use onChange handler to update the value:

React

Vue

Angular

import { useState } from 'react';
import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo() {
  const [value, setValue] = useState<string | null>('Vue');

  return <SelectStepper data={['React', 'Vue', 'Angular']} value={value} onChange={setValue} />;
}

onChange handler

onChange is called with two arguments:

  • value - string value of the selected option
  • option – selected option object

If you prefer object format in state, use second argument of onChange handler:

import { useState } from 'react';
import {
  SelectStepper,
  type ComboboxItem,
} from '@gfazioli/mantine-select-stepper';

function Demo() {
  const [value, setValue] = useState<ComboboxItem | null>({
    value: 'vue',
    label: 'Vue.js Framework',
  });

  const data = [
    { value: 'react', label: 'React JS' },
    { value: 'vue', label: 'Vue.js Framework' },
    { value: 'angular', label: 'Angular Platform' },
  ];

  return (
    <SelectStepper
      data={data}
      value={value ? value.value : null}
      onChange={(_value, option) => setValue(option)}
    />
  );
}

Loop

Enable infinite scrolling by setting the loop prop. When enabled, the stepper will wrap around to the beginning when reaching the end, and vice versa.

React

Vue

Angular

Svelte

Solid

React

Vue

Angular

Svelte

Solid

React

Vue

Angular

Svelte

Solid

import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo() {
  return (
    <SelectStepper 
      data={['React', 'Vue', 'Angular', 'Svelte', 'Solid']} 
      loop 
    />
  );
}

Disabled items

You can disable specific items in the data array. Disabled items will be skipped when navigating through the options.

React

Vue

Angular

Svelte

Solid

import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo() {
  return (
    <SelectStepper
      data={[
        { value: 'react', label: 'React' },
        { value: 'vue', label: 'Vue', disabled: true },
        { value: 'angular', label: 'Angular' },
        { value: 'svelte', label: 'Svelte', disabled: true },
        { value: 'solid', label: 'Solid' },
      ]}
    />
  );
}

Disabled state

Set the disabled prop to disable the entire component. This will prevent any interaction with the stepper.

React

Vue

Angular

import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo() {
  return (
    <SelectStepper 
      data={['React', 'Vue', 'Angular']} 
      defaultValue="Vue"
      disabled 
    />
  );
}

Custom icons

Customize the left and right icons using the leftIcon and rightIcon props. You can use any React node as an icon.

16GB

32GB

64GB

128GB

256GB

import { SelectStepper } from '@gfazioli/mantine-select-stepper';
import { IconMinus, IconPlus } from '@tabler/icons-react';

function Demo() {
  return (
    <SelectStepper
      data={['16GB', '32GB', '64GB', '128GB', '256GB']}
      leftIcon={<IconMinus size={16} />}
      rightIcon={<IconPlus size={16} />}
    />
  );
}

Custom width

Adjust the viewport width using the viewWidth prop. This controls how much space is allocated for displaying the current value.

Short

Medium Width

Very Long Item Name

import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo() {
  return (
    <SelectStepper 
      data={['Short', 'Medium Width', 'Very Long Item Name']} 
      viewWidth={300} 
    />
  );
}

With border

Add a border to the component using the withBorder prop.

React

Vue

Angular

React

Vue

Angular

import { SelectStepper } from '@gfazioli/mantine-select-stepper';
import { Stack } from '@mantine/core';

function Demo() {
  return (
    <Stack>
      {/* With border */}
      <SelectStepper 
        data={['React', 'Vue', 'Angular']} 
        withBorder 
      />
      
      {/* Without border (default) */}
      <SelectStepper 
        data={['React', 'Vue', 'Angular']} 
        withBorder={false} 
      />
    </Stack>
  );
}

Animation

Control the animation behavior with the animate, animationDuration, and animationTimingFunction props. You can create custom timing functions for unique effects, or disable animations entirely.

Fast

Normal

Slow

Bouncy

Instant

Switch

No Animation

import { SelectStepper } from '@gfazioli/mantine-select-stepper';
import { Stack } from '@mantine/core';

function Demo() {
  return (
    <Stack>
      {/* Custom animation duration and timing */}
      <SelectStepper 
        data={['Fast', 'Normal', 'Slow', 'Bouncy']} 
        animationDuration={800} 
        animationTimingFunction="cubic-bezier(0.68, -0.55, 0.265, 1.55)" 
      />
      
      {/* No animation */}
      <SelectStepper 
        data={['Instant', 'Switch', 'No Animation']} 
        animate={false} 
      />
    </Stack>
  );
}

Keyboard navigation

SelectStepper supports keyboard navigation:

  • ArrowLeft or ArrowDown - Move to previous item
  • ArrowRight or ArrowUp - Move to next item

Custom rendering

renderOption prop

Use the renderOption prop to customize how each option is displayed. This function receives a ComboboxItem object and should return a React node. This is useful when you want to add additional visual elements like badges, icons, or custom formatting to your options.

React
(5 chars)
Vue
(3 chars)
Angular
(7 chars)
Svelte
(6 chars)
Solid
(5 chars)
import { SelectStepper } from '@gfazioli/mantine-select-stepper';
import { Badge, Group } from '@mantine/core';

function Demo() {
  return (
    <SelectStepper
      data={['React', 'Vue', 'Angular', 'Svelte', 'Solid']}
      renderOption={(item) => (
        <Group gap="xs">
          <Badge variant="light">{item.label}</Badge>
          <span style={{ fontSize: 12, color: '#888' }}>({item.value.length} chars)</span>
        </Group>
      )}
    />
  );
}

Custom data with extended properties

You can extend the ComboboxItem interface to include additional properties in your data. When using the onChange handler, the second argument will contain the complete option object with all custom properties. This is perfect for storing metadata alongside your options.

{
  "value": "vue",
  "label": "Vue.js",
  "version": "3.2.37",
  "color": "green"
}
React
v18.2.0
Vue.js
v3.2.37
Angular
v13.3.0
import { useState } from 'react';
import { SelectStepper, type ComboboxItem } from '@gfazioli/mantine-select-stepper';
import { Badge, Code, Group, Stack } from '@mantine/core';

interface ComplexItem extends ComboboxItem {
  version?: string;
  color?: string;
}

function Demo() {
  const [value, setValue] = useState<ComplexItem | null>({ 
    value: 'vue', 
    label: 'Vue.js', 
    version: '3.2.37',
    color: 'green'
  });

  const data: ComplexItem[] = [
    { value: 'react', label: 'React', version: '18.2.0', color: 'blue' },
    { value: 'vue', label: 'Vue.js', version: '3.2.37', color: 'green' },
    { value: 'angular', label: 'Angular', version: '13.3.0', color: 'red' },
  ];

  return (
    <Stack>
      <Code block>{JSON.stringify(value, null, 2)}</Code>
      
      <SelectStepper
        data={data}
        value={value ? value.value : null}
        onChange={(_value, option) => setValue(option as ComplexItem)}
        renderOption={(item) => {
          const complexItem = item as ComplexItem;
          return (
            <Group gap="xs">
              <Badge color={complexItem.color || 'gray'}>
                {complexItem.label}
              </Badge>
              <span style={{ fontSize: 11, color: '#888' }}>
                v{complexItem.version}
              </span>
            </Group>
          );
        }}
        viewWidth={280}
      />
    </Stack>
  );
}

Styles API

SelectStepper supports Styles API, you can add styles to any inner element of the component with classNames prop. Follow Styles API documentation to learn more.

This is a description

Hello

This is an error

Component Styles API

Hover over selectors to highlight corresponding elements

/*
 * Hover over selectors to apply outline styles
 *
 */

Use in forms

SelectStepper integrates seamlessly with other Mantine form components. It works well alongside inputs, selects, and other form controls, making it perfect for multi-step forms or configuration panels.

16GB

32GB

64GB

import { Group, Select, Stack, TextInput } from '@mantine/core';
import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo() {
  return (
    <Stack>
      <SelectStepper 
        data={['React', 'Vue', 'Angular']} 
        withBorder 
        placeholder="Select framework"
      />
      <TextInput placeholder="Your name" label="Name" />
      <Select 
        data={['React', 'Vue', 'Angular']} 
        placeholder="Select your favorite framework"
        label="Favorite framework"
      />

      <Group grow>
        <SelectStepper 
          data={['Junior', 'Mid', 'Senior']} 
          withBorder 
          placeholder="Experience level"
        />
        <TextInput placeholder="Years" label="Years of experience" />
      </Group>
    </Stack>
  );
}

Boolean stepper pattern

The SelectStepper can be effectively used as a boolean toggle by combining several features. This pattern is useful when you want to create an interactive on/off switch with enhanced visual feedback.

Key features demonstrated in this example:

  • Dynamic Icons: The leftIcon and rightIcon props change based on the current value, providing visual context
  • Custom Rendering: The renderOption prop displays the option as a colored badge (green for enabled, red for disabled)
  • Loop Navigation: With loop enabled, users can continuously toggle between the two states
  • Controlled State: The component maintains the boolean value as a string ('true' or 'false')

This pattern is particularly useful for:

  • Security settings (enable/disable features)
  • Feature flags and toggles
  • Binary choices with visual distinction
  • Settings that benefit from explicit labeling of both states
Malware Protection
Malware Protection
Malware Protection
Malware Protection
Malware Protection
Malware Protection
import { useState } from 'react';
import { SelectStepper } from '@gfazioli/mantine-select-stepper';
import { IconCheck, IconMinus, IconPlus } from '@tabler/icons-react';
import { Badge } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState<string | null>('false');

  return (
    <SelectStepper
      viewWidth={200}
      animate={false}
      value={value}
      loop
      label={`Boolean Stepper: ${value}`}
      variant="subtle"
      leftIcon={value === 'true' ? <IconCheck size={16} /> : null}
      rightIcon={value === 'false' ? <IconPlus size={16} /> : <IconMinus color="red" size={16} />}
      onChange={setValue}
      data={[
        {
          value: 'false',
          label: 'Malware Protection',
        },
        {
          value: 'true',
          label: 'Malware Protection',
        },
      ]}
      renderOption={(item) => (
        <Badge color={item.value === 'true' ? 'green' : 'red'}>{item.label}</Badge>
      )}
    />
  );
}

Use cases

SelectStepper is particularly useful in scenarios where you have a limited set of sequential options and want to provide an intuitive navigation experience:

  • Settings and Preferences: Choose between predefined options like theme modes (Light/Dark/Auto), text sizes (Small/Medium/Large), or quality levels (Low/Medium/High)
  • Step-by-step Wizards: Navigate through sequential steps in a form or configuration process
  • Rating Systems: Select ratings or levels (Beginner/Intermediate/Advanced, or 1-5 stars)
  • Time Selection: Choose between time slots, hours, or predefined time intervals
  • Difficulty Levels: Select game difficulty, exercise intensity, or skill levels
  • Priority Selection: Choose task priorities (Low/Medium/High/Critical)
  • Size Selection: Pick product sizes (XS/S/M/L/XL) in e-commerce applications
  • View Modes: Switch between different view layouts (List/Grid/Table)
  • Playback Speed: Control media playback speed (0.5x/1x/1.5x/2x)
  • Zoom Levels: Select predefined zoom percentages (50%/75%/100%/125%/150%)

The component is especially effective when:

  • The number of options is relatively small (typically 3-10 items)
  • Options have a natural sequential order
  • Users benefit from seeing the current selection prominently
  • Space is limited and a traditional select dropdown would be too large
  • You want to encourage exploration of available options through keyboard or button navigation