---
title: Slider
description: A native range input with its label - keyboard steps and form value ship in the element.
source: https://adamjurek.com/components/slider
---

## Home


Slider is a native `<input type="range">` with its label attached, painted
through `accent-color`. Reach for it wherever an approximate drag beats a
typed number - volume, brightness, a threshold on a chart.

<!-- ::start:showcase demo="slider" height="380" -->
<!-- ::end:showcase -->

## Why it is built this way

It stays a native range input rather than a rebuilt one because everything a
rebuilt slider spends its life re-earning - keyboard steps, page-up and
page-down jumps, RTL support, participating in a form's value - ships for
free in the browser's own element.


## Install

<!-- ::start:tabs -->

### TanStack

```shell
tanstack add https://adamjurek.com/r/tanstack/slider.json
```

### shadcn

```shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/slider.json
```

### pnpm

```shell
pnpm add @sushindustries/ui @sushindustries/atoms
```

<!-- ::end:tabs -->

## What you get

| | |
| --- | --- |
| Version | 0.1.0 |
| Category | content · Forms |
| Files | `slider.tsx` |
| Dependencies | None |
| Tags | form, no-deps |

> [!NOTE] No runtime dependencies
> It brings nothing with it beyond the stylesheet.

## Get Started


Install commands are on Home, attached from the registry - they are not written
here, because a second copy is a copy that goes stale. This tab starts after the
install worked.

## Use it

```tsx
import { Slider } from "@sushindustries/ui";

export function Example() {
	return <Slider label="Volume" min={0} max={100} defaultValue={35} />;
}
```

## What you should see

The label above a track filled in the accent color from the left edge up to
the thumb. Drag it, click anywhere on the track, or focus it and press an
arrow key or Page Up/Down - all of that is the native `<input type="range">`
doing what it already does, not code this component adds.

## If nothing happens

A track and thumb in flat grey rather than the accent color means the atoms
stylesheet is not loaded - `accent-color` is the only styling this component
applies. Passing `value` without `onChange` gives you a range the browser
will not let anyone move; use `defaultValue` for an uncontrolled slider.


## Guides


## Composing it

`Slider` already returns a `<label>` wrapping the input and its text -
`label` is required for exactly this reason, there is no unlabelled variant.
Do not nest it inside another `Field` or `<label>`; that puts two labels
around one control, and a screen reader announces both.

## When not to use it

For a value someone needs to type precisely - a price, a quantity, a date -
a slider trades away exact entry for a gesture that is fast but imprecise.
Reach for `Input` with `type="number"` there, and save `Slider` for values
where "close enough, seen at a glance" is the actual goal: volume,
brightness, a threshold on a chart.


## API


<!-- generated:api -->

## Props

Accepts every prop of `Omit<InputHTMLAttributes<HTMLInputElement>, "type">`, plus:

| Prop | Type | Default | Does |
| --- | --- | --- | --- |
| `label` | `string` | - |  |

<!-- /generated:api -->

## Notes

`type` is omitted from the props on purpose - the input is always
`type="range"`, so there is nothing to pass that would change it. `min`,
`max` and `step` are not defaulted by this component; leave them off and you
get the browser's own range defaults (0 to 100, step 1), the same as a bare
`<input type="range">`.


## Examples


Examples are the tab where the component is shown doing a job, not
demonstrating a prop. The API tab already lists the props.

<!-- ::start:showcase demo="slider" height="420" -->
<!-- ::end:showcase -->

Press Compare. The frames are real viewports, so a layout that breaks at 320
breaks here too rather than in somebody's hands.

## In a page

```tsx
import { useState } from "react";
import { Slider, Switch } from "@sushindustries/ui";

export function PlaybackSettings() {
	const [volume, setVolume] = useState(70);

	return (
		<form className="flex flex-col gap-4">
			<Slider
				label="Volume"
				min={0}
				max={100}
				value={volume}
				onChange={(event) => setVolume(Number(event.target.value))}
			/>
			<Switch label="Smooth scrolling" defaultChecked />
		</form>
	);
}
```

## What this example is not

`onChange` here reads `event.target.value` as a string and converts it -
that conversion is the caller's job for every native range input, not
something `Slider` does for you.
