react ~17 KB 0 deps v0.3.1 ↗ GitHub ↗

@mshafiqyajid/react-segmented-control

Headless segmented control hook and styled component for React. Buttery sliding indicator, ResizeObserver measurement, full keyboard nav, generic over option type, object options with custom labels. Form-input contract: name + hidden input, label / hint / error / invalid / required.

Playground #

Time range
Props
TSX
import { SegmentedControlStyled } from "@mshafiqyajid/react-segmented-control/styled";
import "@mshafiqyajid/react-segmented-control/styles.css";

<SegmentedControlStyled
  options=[]
  value={value}
  onChange={setValue}
/>

Install #

npm install @mshafiqyajid/react-segmented-control

Quick start #

import { SegmentedControlStyled } from "@mshafiqyajid/react-segmented-control/styled";
import "@mshafiqyajid/react-segmented-control/styles.css";

const [view, setView] = useState("week");

<SegmentedControlStyled
  options={["day", "week", "month"]}
  value={view}
  onChange={setView}
  tone="primary"
/>

Recipes #

Object options with custom labels

<SegmentedControlStyled
  options={[
    { value: "asc", label: "↑ Ascending" },
    { value: "desc", label: "↓ Descending" },
  ]}
  value={sort}
  onChange={setSort}
/>

With disabled options

<SegmentedControlStyled
  options={[
    "Free",
    "Pro",
    { value: "Enterprise", disabled: true },
  ]}
  defaultValue="Free"
/>

Generic over object types

type Plan = { id: string; price: number };

<SegmentedControlStyled<Plan>
  options={plans.map(p => ({ value: p, label: p.id }))}
  value={selected}
  onChange={setSelected}
  equals={(a, b) => a.id === b.id}
/>

Headless #

import { useSegmentedControl } from "@mshafiqyajid/react-segmented-control";

const { options, rootProps, indicatorStyle } = useSegmentedControl({
  options: ["day", "week", "month"],
  defaultValue: "week",
});

return (
  <div {...rootProps} className="seg">
    <span className="seg__indicator" style={indicatorStyle} />
    {options.map((opt) => (
      <button key={String(opt.value)} {...opt.buttonProps}>
        {opt.label}
      </button>
    ))}
  </div>
);

API #

SegmentedControlStyled

PropTypeDefaultDescription
optionsArray<T | { value, label?, disabled? }>Required. Options to render.
valueTControlled value.
defaultValueTfirst optionUncontrolled initial value.
onChange(value: T) => voidFires on change.
equals(a: T, b: T) => booleanObject.isCustom equality for objects.
variantsolid | pill | underlinesolidVisual style.
sizesm | md | lgmdSize.
toneneutral | primary | success | dangerprimaryIndicator color.
fullWidthbooleanfalseStretch to container.
labelReactNodeLabel above.
Edit this page on GitHub