Skip to content
shadcn.io is not affiliated with official shadcn/ui

Shadcn Circular Slider for React and Tailwind

A progress-ring dial for selecting an angle.

45°

Installation

bunx --bun shadcn@latest add https://kit.dev/r/circular-slider.json

Circular Slider is the progress-ring preset of Ark Angle Slider. Same machine (0360°, step, keyboard). The ring, thumb, and hidden input are baked in. Use Angle Slider when you want a needle knob and assembled parts.

Anatomy

CircularSlider
├── Control (baked in)
│   ├── Progress ring
│   ├── MarkerGroup / Marker (when `markers` is set)
│   └── Thumb (baked in)
├── CircularSliderLabel
├── CircularSliderValue
└── HiddenInput (baked in)

useCircularSlider is the machine hook for CircularSliderRootProvider. useCircularSliderContext / CircularSliderContext is in-tree.

Usage

import {
  CircularSlider,
  CircularSliderValue,
} from "@/components/ui/circular-slider";
<CircularSlider aria-label="Angle" defaultValue={45}>
  <CircularSliderValue suffix="°" />
</CircularSlider>

Pass aria-label (or CircularSliderLabel) — the thumb is a role="slider" with no visible name by default.

shadcn.io size defaults to 100 and thickness to 6.

Controlled

Use value and onValueChange. { value } is the angle in degrees.

Root Provider

Use useCircularSlider with CircularSliderRootProvider when you need the API outside the tree. Pass machine options (defaultValue, step, disabled, …) to useCircularSlider(), not to the provider. Pass size / thickness / markers to the provider (layout).

Context

CircularSliderContext / useCircularSliderContext read the angle inside the tree.

States

Disabled

Invalid

Read-only

Focusable but not editable.

Examples

Label

CircularSliderLabel sits in the center of the ring and names the thumb.

Size

size is the diameter in pixels.

Thickness

thickness is the ring stroke width in pixels.

Markers

markers shows clock ticks (every 60°).

Step

step snaps the thumb. Combine markers and markersAtSteps to put a tick on each step.

Value

CircularSliderValue shows the current angle. prefix and suffix wrap the number. Pass children to replace the default readout.

Custom markers

Pass a number[] of angles to markers.

API Reference

shadcn.io wraps Ark UI Angle Slider as a filled-ring control. Control, thumb, ring, and hidden input are baked into CircularSlider / CircularSliderRootProvider.

asChild merges props onto a single child element. The thumb is a role="slider" with values from 0 to 360.

CircularSlider

Root. Renders a div. Sets --value and --angle (machine) plus --size and --thickness (shadcn.io).

PropTypeDefaultDescription
aria-labelstring-Accessible name for the thumb when there is no visible label.
aria-labelledbystring-Id of the element that labels the thumb. Defaults to the label id.
asChildbooleanfalseRender the child element instead of a div.
classNamestring-Class names on the root.
defaultValuenumber0Uncontrolled initial angle in degrees.
dir"ltr" | "rtl"-Text direction. Usually inherited from LocaleProvider.
disabledboolean-Disable pointer and keyboard input.
idstring-Unique id for the machine.
idsPartial<{ root: string; thumb: string; hiddenInput: string; control: string; valueText: string; label: string }>-Element ids for composition.
invalidboolean-Marks the slider as invalid.
markersboolean | number[]-true draws clock ticks. An array is custom angles.
markersAtStepsbooleanfalseWhen markers is true, tick every step instead of 60°.
namestring-Name for form submission (hidden input).
onValueChange(details: ValueChangeDetails) => void-Called when the value changes. { value, valueAsDegree }.
onValueChangeEnd(details: ValueChangeDetails) => void-Called when dragging or stepping ends.
readOnlyboolean-Focusable but not editable.
sizenumber100Diameter in pixels (--size).
stepnumber1Snap increment in degrees.
thicknessnumber6Ring stroke width in pixels (--thickness).
valuenumber-Controlled angle in degrees (0360).
AttributeDescription
data-slotcircular-slider
data-scopeangle-slider
data-partroot
data-disabledPresent when disabled
data-invalidPresent when invalid
data-readonlyPresent when read-only
CSS variableDescription
--valueCurrent numeric value
--angleDisplay angle used to rotate the thumb (accounts for dir)
--sizeDiameter (shadcn.io)
--thicknessRing stroke width (shadcn.io)

CircularSliderRootProvider

Takes the API from useCircularSlider. Same baked ring as CircularSlider.

PropTypeDefaultDescription
valueUseAngleSliderReturnrequiredReturn value of useCircularSlider().
asChildbooleanfalseRender the child element instead of a div.
classNamestring-Class names on the root.
markersboolean | number[]-Same as CircularSlider.
markersAtStepsbooleanfalseSame as CircularSlider.
sizenumber100Diameter in pixels.
stepnumber1Used only to place markersAtSteps ticks. Match the machine step.
thicknessnumber6Ring stroke width in pixels.

Pass defaultValue, step, disabled, name, and other machine options to useCircularSlider(), not to the provider.

CircularSliderLabel

Visible name in the center of the ring. Clicking it focuses the thumb. Renders a label.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the label.
AttributeDescription
data-slotcircular-slider-label
data-scopeangle-slider
data-partlabel
data-disabledPresent when disabled
data-invalidPresent when invalid
data-readonlyPresent when read-only

CircularSliderValue

Current angle. Renders a div. Default content is prefix + value + suffix. Pass children to replace that.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the value.
prefixReactNode-Content before the number.
suffixReactNode-Content after the number.
childrenReactNode-Custom content. Overrides prefix / value / suffix.
AttributeDescription
data-slotcircular-slider-value
data-scopeangle-slider
data-partvalue-text

CircularSliderThumb

Baked into the root. Renders a div with role="slider". Rotated with --angle. The visible knob is a child span on the ring.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the thumb.
AttributeDescription
data-slotcircular-slider-thumb
data-scopeangle-slider
data-partthumb
data-disabledPresent when disabled
data-invalidPresent when invalid
data-readonlyPresent when read-only

aria-valuemin={0}, aria-valuemax={360}, aria-valuenow is the current angle.

CircularSliderMarkerGroup / CircularSliderMarker

Baked when markers is set. You can also render them yourself. Marker value is the angle in degrees.

AttributeDescription
data-slotcircular-slider-marker-group / circular-slider-marker
data-scopeangle-slider
data-partmarker-group / marker
data-valueMarker angle
data-state"under-value", "at-value", or "over-value"

useCircularSlider

Creates the angle slider API for CircularSliderRootProvider. Accepts the same machine options as CircularSlider except layout-only props (size, thickness, markers, markersAtSteps, asChild, className).

const slider = useCircularSlider({ defaultValue: 45, step: 15 });
slider.setValue(90);

CircularSliderContext / useCircularSliderContext

Render-prop or hook access. Use inside CircularSlider or CircularSliderRootProvider.

PropertyTypeDescription
valuenumberCurrent angle in degrees.
valueAsDegreestringFormatted degree string (for example "45deg").
draggingbooleanWhether the thumb is being dragged.
setValue(value: number) => voidSet the angle.

CircularSliderContext children: (context) => ReactNode.

Accessibility

The thumb is a role="slider" with aria-valuemin={0}, aria-valuemax={360}, and aria-valuenow set to the current value. Pair it with CircularSliderLabel, or pass aria-label / aria-labelledby on CircularSlider. The SVG ring is aria-hidden.

Keyboard support

KeyDescription
ArrowRightIncrease the value by step. Respects RTL.
ArrowLeftDecrease the value by step.
ArrowUpDecrease the value by step.
ArrowDownIncrease the value by step.
HomeSet the value to 0.
EndSet the value to 360.
Shift + ArrowLarger step (native event step multiplier).