Knob

A rotary control for continuous parameters, with pointer, wheel and keyboard input.

Audio UI

A rotary control for continuous parameters such as level, cutoff or pan. The registry component wraps the Knob primitive from @audio-ui/react and adds size variants and theme styles.

Overview

  • Input: drag around the knob (dragMode="circular", the default), up and down ("vertical"), or by full turns ("revolution"). The mouse wheel and the keyboard also work. A double-click or a double-tap returns to defaultValue.
  • Display: a track, an arc from anchor to the value, and a needle that turns with the value. The needle follows the pointer without easing, so it stays in line with the arc.
  • Accessibility: the knob holds a native <input type="range"> that takes focus and carries the accessible name (aria-label or aria-labelledby), the value text (formatted with format, or set with getAriaValueText on the primitive's Thumb) and the form value (name).

Installation

pnpm dlx shadcn@latest add @audio-ui/knob

Anatomy

Import the primitive and assemble its parts.

import { Knob } from "@audio-ui/react";

<Knob.Root>
  <Knob.Control>
    <Knob.Track />
    <Knob.Indicator />
    <Knob.Thumb />
    <Knob.Mark value={50} />
  </Knob.Control>
  <Knob.Value />
  <Knob.Label />
</Knob.Root>

Usage

import { Knob } from "@/components/audio-ui/knob";

Uncontrolled: set only the initial value.

<Knob defaultValue={50} max={100} min={0} step={1} />

Controlled: drive the value from state.

const [cutoff, setCutoff] = useState(1000);

<Knob
  max={20_000}
  min={20}
  onValueChange={setCutoff}
  step={1}
  value={cutoff}
/>

Pass defaultValue even when the knob is controlled if you use the double-tap reset: it sets the value the knob returns to, clamped and stepped.

Interaction

Pointer

Behavior · 5

Circular drag (default)
The value follows the rotation around the center, from the value at pointer down. The dial's 288° sweep spans min…max.
Vertical drag
dragMode="vertical": drag up to increase, like a fader; 150px of travel spans min…max.
Revolution drag
dragMode="revolution": one full 360° turn spans min…max.
Fine drag
Hold Shift while dragging, in any mode, to turn four times slower. Only the stretch moved with Shift held is slowed, and small moves add up rather than rounding away.
Double-tap / double-click
Two quick taps (each moving ≤ ~12px, within ~320ms) reset to defaultValue, or to the middle of the dial's travel without one. Reports eventDetails.reason === "reset".

Values stay within min…max and do not wrap. While dragging, the knob follows the pointer even if a controlled value updates late.

Scale

scale sets how the value maps onto the dial's travel. "linear" (the default) suits levels and times; "log" suits frequency, resonance and ratios, which are heard as proportions: on a 20 Hz–20 kHz cutoff, every decade gets the same share of the dial, and 632 Hz sits at the middle. A log scale needs a positive min. For anything else, pass your own { toPosition, toValue }, mapping a value to 0…1 along the travel and back.

<Knob aria-label="Cutoff" max={20_000} min={20} scale="log" />

Away from linear, the keys and the wheel move a share of the travel (1%, or 10% with Shift and the Page keys) rather than a step, and always by at least one step.

Keyboard

On the focused knob:

Keys · 6

ArrowUp / ArrowRight
Increase by step
ArrowDown / ArrowLeft
Decrease by step
PageUp / Shift + ArrowUp
Increase by largeStep (default 10)
PageDown / Shift + ArrowDown
Decrease by largeStep (default 10)
Home
Set to min
End
Set to max

Wheel

The mouse wheel over the knob moves the value by step and reports eventDetails.reason === "wheel".

API reference

Root

Groups all parts of the knob and owns its value. Dragging the Control, turning the mouse wheel over it, the keyboard on the Thumb's input, or a double tap (reset) all report through onValueChange / onValueCommitted. Renders a <div> element.

Props · 16

Data attributes · 2

data-disabled
Present when disabled.
data-dragging
Whether a pointer is dragging the knob.

Control

The interactive surface: pointer drags (see dragMode), mouse-wheel stepping, and double tap to reset. Sets touch-action: none so touch drags don't scroll the page. Renders a <div> element.

Data attributes · 2

data-disabled
Present when disabled.
data-dragging
Whether a pointer is dragging the knob.

Track

The full sweep of the dial, as an SVG arc. Renders a <svg> element.

Props · 1

Data attributes · 2

data-disabled
Present when disabled.
data-dragging
Whether a pointer is dragging the knob.

Indicator

The arc from anchor to the value, drawn over the Track. Renders a <svg> element.

Data attributes · 2

data-disabled
Present when disabled.
data-dragging
Whether a pointer is dragging the knob.

Thumb

Rotates with the value (rotate, clockwise from 12 o'clock), so a pointer drawn inside it points at the value. Holds the knob's native <input type="range">, which takes focus, carries the accessible name (aria-label / aria-labelledby) and the form value, and handles the keyboard. Renders a <div> element.

Props · 2

Data attributes · 2

data-disabled
Present when disabled.
data-dragging
Whether a pointer is dragging the knob.

Value

The formatted value (format/locale on Root). With editable, a double click, or Enter while it has focus, lets you type a value: Enter commits it (a unit or a decimal comma is fine, and it is clamped), Escape drops it, and it reports reason: "input-change". Renders a <output> element.

Props · 2

Data attributes · 2

data-disabled
Present when disabled.
data-dragging
Whether a pointer is dragging the knob.

Mark

A graduation on the dial at value, placed on the knob's scale: a layer the size of the knob, rotated to where value sits, so a tick drawn at its top lands on the sweep. Its --knob-mark-angle CSS variable is that rotation, to turn a label back upright. It carries data-active while the value has reached it from the anchor. Decorative: the input announces the value. Renders a <div> element.

Props · 1

Data attributes · 3

data-disabled
Present when disabled.
data-dragging
Whether a pointer is dragging the knob.
data-active
Whether the value has reached this mark from the anchor.

Label

Accessible label, associated with the thumb's input. Renders a <div> element.

Data attributes · 2

data-disabled
Present when disabled.
data-dragging
Whether a pointer is dragging the knob.

Registry component

Knob accepts size and className plus primitive props from KnobPrimitive.Root.Props. aria-label, aria-labelledby, and aria-describedby are forwarded to the knob's input.

Props · 21

@audio-ui/react/knob exposes Knob.Root, Knob.Control (pointer, wheel, reset), Knob.Track and Knob.Indicator (SVG arcs; strokeWidth in the 48×48 viewBox), Knob.Thumb (rotates with the value and holds the input), Knob.Value (formatted <output>), Knob.Mark (a graduation at a value, rotated onto the dial's scale, with data-active once reached), and Knob.Label. Every part accepts a render prop, className / style as functions of state, and exposes data-disabled / data-dragging.

Examples

Rail, anchor, and range highlight

Use anchor to show a reference point on the rail (for example the center of a bipolar control). The arc highlights the interval between anchor and the current value.

Audio UI

Logarithmic scale

scale="log" spreads a frequency evenly by ratio, so a quarter turn goes from 1 kHz to 8.7 kHz instead of packing the useful range into the first few degrees.

Audio UI

More Shadcn Knob components

Browse 25 production-ready audio/ui Knob components for players, mixers, and synth interfaces. These examples use the headless primitives from @audio-ui/react and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.

Browse all 25 Shadcn Knob components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the audio/ui library.

Built by Ouest Labs, the source code is available on GitHub.
RSS