Toggle from a set of two or more options. SegmentedControl is essentially a styled wrapper around a group of radio inputs.

Use this component when:

  • It's important to see all the options
  • It is expected to be a frequent back and forth
  • Horizontal treatment is better visually than vertical
  • 2-4 items

Use a RadioGroup when:

  • It's important to see all the options
  • It's expected the user will not change this choice often
  • It's not going to stay present on the page (i.e. during set up)
  • 2+ items

Importing

The component can be imported via:

import { SegmentedControl } from '@customerio/pluma-components/react';

Usage

The component accepts an array of options, defined by the SegmentedControlOption interface. A value and label (or ariaLabel) are, at a minimum, required for each option.

Additionally, a label (or ariaLabel) is required on the SegmentedControl itself, which provides information on what is being selected.

The control items internally render as radio inputs. Because of this, it's also recommended to provide a name prop to the component, to tie the radio inputs together. If no name is provided, one will be generated.

Finally, value and onChange are also required to control the currently selected option. value should be the value of the currently selected option.

Filter by
import { SegmentedControl } from '@customerio/pluma-components/react';
import { useState } from 'react';

export default function Example() {
  const [value, setValue] = useState('email');

  return <SegmentedControl
    label="Filter by"
    name="filter_by_react"
    value={value}
    onChange={(value) => setValue(value)}
    options={[
      { value: 'email', label: 'email address' },
      { value: 'conditions', label: 'conditions' },
    ]}
  />
}

Style variant

By default, the component renders a gray background for the selected option. For a version of the component with more visual weight, variant="primary" is available.

This can be used when you need to draw more attention to the control. This variant could be distracting when other primary-style buttons are present on the page, and should be used with caution.

Control label
import { SegmentedControl } from '@customerio/pluma-components/react';
import { useState } from 'react';

export default function Example() {
  const [value, setValue] = useState('value_1');

  return <SegmentedControl
    label="Control label"
    name="snippet_primary"
    variant="primary"
    value={value}
    onChange={(value) => setValue(value)}
    options={options}
  />
}

Size

The component supports a size prop, which can be set to small for a smaller version of it:

Control label
import { SegmentedControl } from '@customerio/pluma-components/react';
import { useState } from 'react';

export default function Example() {
  const [value, setValue] = useState('value_1');

  return <SegmentedControl
    label="Control label"
    name="snippet_small"
    size="small"
    value={value}
    onChange={(value) => setValue(value)}
    options={options}
  />
}

Disabled

The component can be disabled via isDisabled:

Control label
import { SegmentedControl } from '@customerio/pluma-components/react';
import { useState } from 'react';

export default function Example() {
  const [value, setValue] = useState('value_1');

  return <SegmentedControl
    label="Control label"
    name="snippet_disabled"
    isDisabled={true}
    value={value}
    onChange={(value) => setValue(value)}
    options={options}
  />
}

Full-width

By default, the component renders as an inline block element. To render it as a full-width block, use the isFullWidth prop:

Control label
import { SegmentedControl } from '@customerio/pluma-components/react';
import { useState } from 'react';

export default function Example() {
  const [value, setValue] = useState('value_1');

  return <SegmentedControl
    label="Control label"
    name="snippet_full_width"
    isFullWidth={true}
    value={value}
    onChange={(value) => setValue(value)}
    options={options}
  />
}

Icons

The segmented control's options accept an icon key, which should be a valid Pluma Icon name.

Control label
import { SegmentedControl } from '@customerio/pluma-components/react';
import { useState } from 'react';

const options = [
  { value: 'campaigns', label: 'Campaigns', icon: 'campaigns' },
  { value: 'newsletter', label: 'Newsletter', icon: 'newsletter' },
  { value: 'transactional-api', label: 'Transactional', icon: 'transactional-api' },
];

export default function Example() {
  const [value, setValue] = useState('campaigns');

  return <SegmentedControl
    label="Control label"
    name="snippet_icons"
    value={value}
    onChange={(value) => setValue(value)}
    options={options}
  />
}

It is also possible to render icon-only options, but an ariaLabel on each option will still be required:

Control label
import { SegmentedControl } from '@customerio/pluma-components/react';
import { useState } from 'react';

const options = [
  { value: 'campaigns', ariaLabel: 'Campaigns', icon: 'campaigns' },
  { value: 'newsletter', ariaLabel: 'Newsletter', icon: 'newsletter' },
  { value: 'transactional-api', ariaLabel: 'Transactional', icon: 'transactional-api' },
];

export default function Example() {
  const [value, setValue] = useState('campaigns');

  return <SegmentedControl
    label="Control label"
    name="snippet_icon_only"
    value={value}
    onChange={(value) => setValue(value)}
    options={options}
  />
}

Generally we don't recommend using icon-only options, as icons may not be universally recognizable in regards to its meaning.

Grid Layout / Columns

The columns prop can be used to arrange the segmented control items into a grid. This is useful when you have a larger number of options that might not fit well in a single row. The value of columns specifies the number of columns in the grid.

Control label
import { SegmentedControl } from '@customerio/pluma-components/react';
import { useState } from 'react';

const options = [
  { value: 'overlay-bottom-left', ariaLabel: 'Overlay Bottom Left', icon: 'overlay-bottom-left' },
  { value: 'overlay-bottom-middle', ariaLabel: 'Overlay Bottom Middle', icon: 'overlay-bottom-middle' },
  { value: 'overlay-bottom-right', ariaLabel: 'Overlay Bottom Right', icon: 'overlay-bottom-right' },
  { value: 'overlay-top-left', ariaLabel: 'Overlay Top Left', icon: 'overlay-top-left' },
  { value: 'overlay-top-middle', ariaLabel: 'Overlay Top Middle', icon: 'overlay-top-middle' },
  { value: 'overlay-top-right', ariaLabel: 'Overlay Top Right', icon: 'overlay-top-right' },
];

export default function Example() {
  const [value, setValue] = useState('overlay-bottom-left');

  return <SegmentedControl
    label="Control label"
    variant="primary"
    name="snippet_grid_layout_icons"
    columns={3}
    value={value}
    onChange={(value) => setValue(value)}
    options={options}
  />
}

Customizing labels

If a string label is not sufficient, a component can be used to render the label.

In React, the label prop accepts any ReactNode.

Custom label
import { SegmentedControl, Icon } from '@customerio/pluma-components/react';
import { useState } from 'react';

export default function Example() {
  const [value, setValue] = useState('option1');
  const options = [
    { value: 'option1', label: 'Option 1' },
    { value: 'option2', label: 'Option 2' },
  ];

  return <SegmentedControl
    label={<>Custom label <Icon name="campaigns" isInline={true} size="sm" /></>}
    name="custom_label_control_react"
    value={value}
    onChange={(value) => {
      setValue(value);
    }}
    options={options}
  />
}

API

The aria-label attribute to be applied on the fieldset.

The aria-labelledby attribute to be applied on the fieldset.

Number of columns to display in a grid layout. When set to a value of 1 or greater, the control will render as a grid with proper ARIA grid semantics and keyboard navigation. When undefined, the control behaves as a traditional horizontal segmented control.

An optional id attribute to assign to the fieldset.

Whether the entire group is disabled.

Whether the control should take up the full width of its container.

The label text to be shown above the control.

The name shared by controls in this group. If none is provided, one will be generated.

An array of options to show in the component.

Default:'md'

The size of the control. medium and small are deprecated, use md and sm instead.

Additional classes to apply to the option item elements (each array item corresponding to the options array).

Additional classes to apply to the option item elements' input elements (each array item corresponding to the options array).

Default:'default'

The style variant of the control.

An aria-label applied to the option. Use when "label" is not descriptive enough for screen readers, or when the control only shows an icon.

The aria-labelledby attribute to be applied on the fieldset, if label and ariaLabel aren't suitable.

The name of an icon (from Pluma Icons) to render in the option.

The source URL of a custom image icon to render in the option. When provided, this will be rendered using the Image component with icon sizing. The image will always have alt="" as it is purely decorative.

Whether this specific option is disabled.

The label text to show within this option.

The value associated with this option.