Slider
A range input component that allows users to select a value from a predefined range by dragging a thumb.
import {
Slider,
SliderLabel,
SliderValueText,
SliderControl,
SliderThumb,
} from "~/components/slider";
export default function SliderBasicDemo() {
return (
<Slider defaultValue={[50]} min={0} max={100}>
<div class="flex items-center justify-between gap-4">
<SliderLabel>Volume</SliderLabel>
<SliderValueText />
</div>
<SliderControl>
<SliderThumb index={0} />
</SliderControl>
</Slider>
);
}Installation
CLI
Run the following command to add the component to your project:
npx @ark-preset/cli@latest add sliderManual
Create the recipe file at src/components/recipes/slider.ts:
import { tv, type VariantProps } from "tailwind-variants";
export const sliderVariants = tv({
slots: {
root: "flex flex-col gap-1.5 w-full",
valueText: "text-base md:text-sm font-medium",
control: "relative flex items-center w-full h-5",
track: "h-1.5 w-full rounded-full bg-muted overflow-hidden",
range:
"h-full rounded-full bg-primary transition-[left,right,width] duration-200 data-[dragging]:transition-[left,right,width] data-[dragging]:duration-75",
thumb:
"size-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-[left,right,width,height,box-shadow] duration-200 hover:size-6 data-[dragging]:size-6 data-[dragging]:ring-4 data-[dragging]:ring-ring/30 data-[dragging]:transition-[left,right,width,height,box-shadow] data-[dragging]:duration-75 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
draggingIndicator: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
markerGroup: "flex justify-between w-full px-0.5",
marker: "text-xs text-muted-foreground",
hiddenInput: "sr-only",
},
variants: {
disabled: {
true: {
thumb: "disabled:pointer-events-none disabled:opacity-50",
},
},
},
defaultVariants: {
disabled: false,
},
});
export type SliderVariants = VariantProps<typeof sliderVariants>;Create the component directory and files.
src/components/slider/slider.base.tsx:
import { Slider as ArkSlider } from "@ark-ui/solid/slider";
import { splitProps, type Component } from "solid-js";
import { sliderVariants, labelVariants } from "../recipes/slider";
const styles = sliderVariants();
const Root: Component<ArkSlider.RootProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSlider.Root class={styles.root({ class: local.class })} {...others} />;
};
const RootProvider: Component<ArkSlider.RootProviderProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSlider.RootProvider class={styles.root({ class: local.class })} {...others} />;
};
const Label: Component<ArkSlider.LabelProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSlider.Label class={labelVariants({ class: local.class })} {...others} />;
};
const ValueText: Component<ArkSlider.ValueTextProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSlider.ValueText class={styles.valueText({ class: local.class })} {...others} />;
};
const Control: Component<ArkSlider.ControlProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSlider.Control class={styles.control({ class: local.class })} {...others} />;
};
const Track: Component<ArkSlider.TrackProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSlider.Track class={styles.track({ class: local.class })} {...others} />;
};
const Range: Component<ArkSlider.RangeProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSlider.Range class={styles.range({ class: local.class })} {...others} />;
};
const Thumb: Component<ArkSlider.ThumbProps> = (props) => {
const [local, others] = splitProps(props, ["class", "index"]);
return (
<ArkSlider.Thumb class={styles.thumb({ class: local.class })} index={local.index} {...others} />
);
};
const HiddenInput: Component<ArkSlider.HiddenInputProps> = (props) => {
return <ArkSlider.HiddenInput {...props} />;
};
const DraggingIndicator: Component<ArkSlider.DraggingIndicatorProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return (
<ArkSlider.DraggingIndicator
class={styles.draggingIndicator({ class: local.class })}
{...others}
/>
);
};
const MarkerGroup: Component<ArkSlider.MarkerGroupProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSlider.MarkerGroup class={styles.markerGroup({ class: local.class })} {...others} />;
};
const Marker: Component<ArkSlider.MarkerProps> = (props) => {
const [local, others] = splitProps(props, ["class", "value"]);
return (
<ArkSlider.Marker
class={styles.marker({ class: local.class })}
value={local.value}
{...others}
/>
);
};
export const Slider = {
Root,
RootProvider,
Label,
ValueText,
Control,
Track,
Range,
Thumb,
HiddenInput,
DraggingIndicator,
MarkerGroup,
Marker,
};src/components/slider/index.tsx:
import { splitProps, type Component } from "solid-js";
import { Slider as SliderBase } from "./slider.base";
import { Slider as ArkSlider } from "@ark-ui/solid/slider";
// Root aliases (backward compat)
const Slider = SliderBase.Root;
const SliderRootProvider = SliderBase.RootProvider;
const SliderLabel = SliderBase.Label;
const SliderValueText = SliderBase.ValueText;
// Composite wrappers — auto-include sub-parts
const SliderControl: Component<ArkSlider.ControlProps> = (props) => {
const [local, others] = splitProps(props, ["children"]);
return (
<SliderBase.Control {...others}>
<SliderBase.Track>
<SliderBase.Range />
</SliderBase.Track>
{local.children}
</SliderBase.Control>
);
};
const SliderThumb: Component<ArkSlider.ThumbProps> = (props) => {
const [local, others] = splitProps(props, ["children", "index"]);
return (
<SliderBase.Thumb index={local.index} {...others}>
{local.children}
<SliderBase.HiddenInput />
</SliderBase.Thumb>
);
};
// Raw parts exported for advanced use
const SliderMarker = SliderBase.Marker;
const SliderMarkerGroup = SliderBase.MarkerGroup;
const SliderDraggingIndicator = SliderBase.DraggingIndicator;
export {
Slider,
SliderRootProvider,
SliderLabel,
SliderValueText,
SliderControl,
SliderThumb,
SliderMarker,
SliderMarkerGroup,
SliderDraggingIndicator,
SliderBase,
};
export { sliderVariants, type SliderVariants } from "../recipes/slider";Note: Make sure your project has the Tailwind CSS theme variables set up (
--background,--foreground,--ring,--border, etc.) or override the utility classes to match your design system.
Usage
Basic Usage
import {
Slider,
SliderLabel,
SliderValueText,
SliderControl,
SliderThumb,
} from "~/components/slider";
export default function SliderBasicDemo() {
return (
<Slider defaultValue={[50]} min={0} max={100}>
<div class="flex items-center justify-between gap-4">
<SliderLabel>Volume</SliderLabel>
<SliderValueText />
</div>
<SliderControl>
<SliderThumb index={0} />
</SliderControl>
</Slider>
);
}Disabled
Use the disabled prop to disable the slider.
import { Slider, SliderLabel, SliderControl, SliderThumb } from "~/components/slider";
export default function SliderDisabledDemo() {
return (
<Slider defaultValue={[50]} disabled>
<SliderLabel>Disabled Slider</SliderLabel>
<SliderControl>
<SliderThumb index={0} />
</SliderControl>
</Slider>
);
}Advanced Usage
Root Provider
Use SliderRootProvider when you need to access the slider state outside of the component tree. This pattern uses the useSlider hook from Ark UI to create a shared context that both the slider and external elements can reference.
import { useSlider } from "@ark-ui/solid/slider";
import {
SliderRootProvider,
SliderLabel,
SliderValueText,
SliderControl,
SliderThumb,
} from "~/components/slider";
export default function SliderRootProviderDemo() {
const slider = useSlider({ defaultValue: [50], min: 0, max: 100 });
return (
<div class="space-y-4">
<output class="block text-sm text-muted-foreground">
Value: {JSON.stringify(slider().value)}
</output>
<SliderRootProvider value={slider}>
<div class="flex items-center justify-between gap-4">
<SliderLabel>Volume</SliderLabel>
<SliderValueText />
</div>
<SliderControl>
<SliderThumb index={0} />
</SliderControl>
</SliderRootProvider>
</div>
);
}The key difference:
Slider(Root) — manages its own state internally. Use for simple, self-contained sliders.SliderRootProvider— accepts a pre-created slider context viauseSlider. Use when you need to read or control the slider state from outside the component tree.
API Reference
See the Ark UI Slider documentation.