Ask AI about this page

Slider Component

Introduction

The slider component provides a powerful and intuitive way to select numeric values or ranges through an interactive draggable interface. Built on top of the robust noUiSlider library, it features support for single and multiple handles, tooltips, visual track fills, pips (value markers), custom formatting, non-linear scales, and full accessibility support. Perfect for price ranges, ratings, measurements, or any numeric input scenario.

Installation

Use the sheaf artisan command to install the slider component easily:

php artisan sheaf:install slider

install the nouislider package

npm install nouislider

then import the component javascripts:

// app.js
import './components/slider.js'

import the css:

/* app.css */
@import './components/slider.css';

Basic Usage

<x-ui.slider 
    wire:model="volume"
    :step="1"
    :fill-track="[true, false]" 
/>

Bind To Livewire

To use with Livewire, simply use wire:model to bind your state:

<!-- this asume you have something like `public $volume = [50]` in your bounded livewire component  -->
<x-ui.slider 
    wire:model.live="volume"
/>

Using it within Blade & Alpine

You can use it outside Livewire with just Alpine (with Blade):

<div class="w-full" x-data="{ volume: [50] }">
    <x-ui.slider 
        x-model="volume"
    />
</div>

Because we're making this possible using the x-modelable like but not explicitly API, you can't use state as a variable name because the component uses it internally.

Visual Customization

Handle Variants

Choose between different handle styles to match your design.

Default Handle

Circle Handle

<!-- Default handle -->
<x-ui.slider 
    wire:model="volume"
    :step="1"
/>

<!-- Circle handle -->
<x-ui.slider 
    wire:model="volume"
    :step="1"
    handleVariant="circle"
/>

Track Fills

Visually highlight portions of the track with color fills.

Single Handle Fill

<x-ui.slider 
    wire:model="volume"
    :fill-track="[true, false]"
/>

Multiple Handle Fills

Control which segments between handles are filled by passing an array of boolean values.

<x-ui.slider 
    wire:model="range"
    :fill-track="[false, true, false]"
/>

Tooltips

Display dynamic tooltips showing the current value of each handle.

Basic Tooltips

<x-ui.slider 
    wire:model="value"
    :step="1"
    tooltips
/>

Custom Tooltip Formatting

Use the $slider.formatTooltipUsing() method via x-init to customize tooltip display.

you can use the formatTooltipUsing() without $slider but it's there for better DX and understandable code

<x-ui.slider 
    wire:model="price"
    tooltips
    x-init="$slider.formatTooltipUsing((value) => '$' + value.toFixed(2))"
/>

Time Format Example

<x-ui.slider 
    wire:model="meetingTime"
    :max-value="24"
    :step="0.25"
    tooltips
    x-init="
        $slider.formatTooltipUsing((value) => {
            const h = Math.floor(value);
            const m = Math.round((value - h) * 60);
            return h.toString().padStart(2, '0') + ':' + m.toString().padStart(2, '0');
        });
    "
/>
Calculating the step Value for a 24-Hour Slider

When you want a slider to represent time in 24 hours, and you want each step to represent a fixed interval (like 15 minutes, 30 minutes, etc.), you can calculate the step value in hours using this formula: step = interval in minutes / 60 example of 15 min per step : s = 15 / 60 = 0.25

Percentage Format Example

<x-ui.slider 
    wire:model="completion"
    tooltips
    x-init="$slider.formatTooltipUsing((value) => value.toFixed() + '%')"
/>

Range Configuration

Setting Min/Max Values

Define the boundaries of your slider with custom minimum and maximum values.

Value:

<x-ui.slider 
    wire:model="temperature"
    :min-value="20"
    :max-value="80"
    :fill-track="[true, false]"
    :step="1"
    tooltips
/>
<p class="mt-2 text-sm text-neutral-600">Value: <span wire:text="range"></span></p>

Step Size

Control the increment between selectable values using the step attribute.

Value: $

<x-ui.slider 
    wire:model="price"
    :fill-track="[true, false]"
    :step="10"
/>
<p class="mt-2 text-sm text-neutral-600">Value: $<span wire:text="price"></span></p>

Decimal Places

For precise control without step restrictions, specify the number of decimal places.

Value:

<x-ui.slider 
    wire:model="measurement"
    :max-value="10"
    :decimalPlaces="2"
    :fill-track="[true, false]"
    tooltips
/>

Range Padding

Add behavioral padding to prevent values from reaching the absolute edges of the track.

Value: (Range: 10-90)

<x-ui.slider 
    wire:model="value"
    :fill-track="[true, false]"
    :step="1"
    :range-padding="10"
    tooltips
/>
<p class="mt-2 text-sm text-neutral-600">Value: <span wire:text="value"></span> (Range: 10-90)</p>

For asymmetric padding, pass an array with start and end values:

Value: (Range: 10-70)

<x-ui.slider 
    wire:model="value"
    :range-padding="[10, 30]"
    tooltips
/>

Multiple Handles

Create range selectors with multiple draggable handles by providing an array of values.

Range: $ - $

 <!-- you may have $priceRange = [20, 75] -->
<x-ui.slider 
    wire:model="priceRange"
    :fill-track="[false, true, false]"
    tooltips
/>
<p class="mt-2 text-sm text-neutral-600">Range: $<span wire:text="priceRange[0]"></span> - $<span wire:text="priceRange[1]"></span></p>

Handle Constraints

Minimum Distance Between Handles

Ensure handles maintain a minimum distance from each other.

Range: - (Min gap: 10)

<x-ui.slider 
    wire:model="range"
    :margin="10"
    :step="1"
    :fill-track="[false, true, false]"
    tooltips
/>
<p class="mt-2 text-sm text-neutral-600">Range: <span wire:text="range[0]"></span> - <span wire:text="range[1]"></span> (Min gap: 10)</p>

Maximum Distance Between Handles

Limit the maximum distance between handles using the limit attribute.

Range: - (Max gap: 30)

<x-ui.slider 
    wire:model="range"
    :limit="30"
    :step="1"
    :fill-track="[false, true, false]"
    tooltips
/>
<p class="mt-2 text-sm text-neutral-600">Range: <span wire:text="range[0]"></span> - <span wire:text="range[1]"></span> (Max gap: 30)</p>

Pips (Value Markers)

Add visual markers along the track to help users identify specific values.

Basic Pips

<x-ui.slider 
    wire:model="value"
    :step="1"
    pips
/>

Pip Density

Control how frequently pips appear using the pipsDensity attribute. Higher values = fewer pips.

Density: 5 (More pips)

Density: 20 (Fewer pips)

<!-- More frequent pips -->
<x-ui.slider 
    wire:model="value"
    :pipsDensity="5"
    tooltips
    :step="1"
    pips
/>

<!-- Less frequent pips -->
<x-ui.slider 
    wire:model="value"
    :pipsDensity="20"
    tooltips
    :step="1"
    pips
/>

Pip Modes

Steps Mode

Display pips at every step interval.

<x-ui.slider 
    x-model="value"
    :step="12"
    :max-value="96"
    pips
    pipsMode="steps"
    :pipsDensity="1"
/>

Positions Mode

Place pips at specific percentage positions along the track.

<x-ui.slider 
    wire:model="value"
    pips
    :step="25/3"
    pipsMode="positions"
    :pipsValues="[0, 25, 50, 75, 100]"
/>

Count Mode

Display a specific number of evenly distributed pips.

<x-ui.slider 
    wire:model="value"
    pips
    pipsMode="count"
    :pipsValues="5"
/>

this is may intrduce large digits if the path length is divisible by the count so in this case the pip label formatter is required:

<x-ui.slider 
    x-model="value"
    pips
    pipsMode="count"
    :pipsValues="13"
    x-init="$slider.formatPipValueUsing((value) => value.toFixed(2))"
/>

Values Mode

Place pips at exact slider values.

<x-ui.slider 
    wire:model="value"
    pips
    pipsMode="values"
    :max-value="150" <!-- to ilustrate that is value based not percentage-->
    :pipsValues="[0, 20, 40, 60, 80, 100]"
/>

Custom Pip Label Formatting

Format pip labels using the $slider.formatPipValueUsing() method.

<x-ui.slider 
    wire:model="price"
    :max-value="500"
    :step="50"
    pips
    pipsMode="steps"
    x-init="$slider.formatPipValueUsing((value) => '$' + value)"
/>

Custom Pip Filtering

Fine-tune which pips are displayed and their size using filterPipsUsing(). Return values:

  • 1 = Large pip with label
  • 2 = Small pip without label
  • 0 = Pip without label
  • -1 = Hidden
<x-ui.slider 
    wire:model="budget"
    pipsMode="steps"
    :max-value="500"
    :pipsDensity="1"
    :step="5"
    tooltips
    pips
    x-init="
        $slider.formatTooltipUsing((value) => '$' + value.toFixed(0));
        $slider.formatPipValueUsing((value) => '$' + value);
        $slider.filterPipsUsing((value, type) => {
            if (value < 50) return -1;        // Hide below $50
            if (value % 100 === 0) return 1;  // Large pip every $100
            if (value % 50 === 0) return 2;   // Small pip every $50
            return 0;                          // Hide others
        });
    "
/>

Stepped Pips

For non-linear sliders, ensure pip labels only appear at selectable stepped positions.

<x-ui.slider 
    :nonLinearPoints="['20%' => 50, '50%' => 75]"
    :arePipsStepped="true"
    wire:model="value"
    pips
/>

Orientation & Direction

Vertical Sliders

Display the slider vertically instead of horizontally.

<x-ui.slider 
    wire:model="volume"
    :fill-track="[true, false]"
    tooltips
    vertical
    :step="1"
/>

Top-to-Bottom Orientation

Reverse the direction of a vertical slider so minimum is at the top.

<x-ui.slider 
    wire:model="temperature"
    top-to-bottom
    :step="1"
    vertical
    tooltips
/>

Right-to-Left

Force the slider to operate right-to-left (useful for RTL languages).

<x-ui.slider 
    wire:model="value"
    :step="1"
    tooltips
    rtl
/>

Circle Variant

<x-ui.slider 
    wire:model="temperature"
    handle-variant="circle"
    top-to-bottom
    :step="1"
    vertical
    tooltips
/>

Advanced Features

Non-Linear Tracks

Create sliders where certain portions of the track represent different value ranges.

This was buggy in the original library because the slider can return strings or decimals even specifying the step to int. We need to manually fix it to integers.

Value (buggy):

Value (manually):

 <x-ui.slider 
    wire:model="value"
    :nonLinearPoints="['30%' => 50, '70%' => 80]"
    x-init="$slider.formatTooltipUsing((val)=>val.toFixed())"
    :step="1"
    pips
    tooltips
/>
<p class="mt-6 text-sm text-neutral-600">Value (buggy): <span wire:text="value"></span></p>
<p class="mt-2 text-sm text-neutral-600">Value (manually): <span wire:text="Number(value).toFixed()"></span></p>

In this example:

  • 0-30% of the track represents values 0-50
  • 30-70% of the track represents values 50-80
  • 70-100% of the track represents values 80-100

Behavior Customization

Control how users interact with the slider using the behavior attribute. Available options:

  • tap - Click anywhere on the track to move the handle (default)
  • drag - Allow dragging the filled portion between handles
  • drag-fixed - Drag both handles together maintaining their distance
  • none - Disable click-to-move behavior

Try dragging the filled area between handles

<x-ui.slider 
    wire:model="range"
    behavior="drag"
    :step="1"
    :fill-track="[false, true, false]"
/>

Disabled State

Disable user interaction with the slider.

<x-ui.slider 
    wire:model="value"
    x-init="$slider.disable()"
    disabled
/>

you may pass the slider index (starting from 0) to the disable function for disabling specific hanle (eg $slider.disable(1)):

Real-World Examples

Price Range Filter

Filter Products by Price

Showing products from $ to $
<x-ui.slider 
    wire:model="priceRange"
    :max-value="1000"
    :step="10"
    pips
    pipsMode="steps"
    :pipsDensity="2"
    tooltips
    :fill-track="[false, true, false]"
    handleVariant="circle"
    x-init="
        formatTooltipUsing((value) => '$' + value);
        formatPipValueUsing((value) => '$' + value);
        filterPipsUsing((value, type) => {
            if (value % 250 === 0) return 1;
            if (value % 100 === 0) return 2;
            return 0;
        });
    "
/>
 <div class="mt-4 text-center">
    <span class="text-neutral-700">Showing products from 
        <strong class="text-neutral-950 dark:text-white">$<span  wire:text="priceRange[0]"></span></strong> to 
        <strong class="text-neutral-950 dark:text-white">$<span  wire:text="priceRange[1]"></span></strong>
    </span>
</div>

Volume Control

<x-ui.slider 
    wire:model="volume"
    tooltips
    :fill-track="[true, false]"
    x-init="$slider.formatTooltipUsing((value) => value + '%')"
/>

Meeting Time Scheduler

Select Meeting Hours

Meeting available from to
<x-ui.slider 
    wire:model="meetingTime"
    :max-value="23"
    :step="1"
    pips
    pipsMode="steps"
    :pipsDensity="3"
    tooltips
    :fill-track="[false, true, false]"
    x-init="
        formatTooltipUsing((value) => {
            const hour = value === 0 ? 12 : (value > 12 ? value - 12 : value);
            const period = value < 12 ? 'AM' : 'PM';
            return hour + ':00 ' + period;
        });
        formatPipValueUsing((value) => {
            if (value === 0) return '12AM';
            if (value === 12) return '12PM';
            const hour = value > 12 ? value - 12 : value;
            const period = value < 12 ? 'AM' : 'PM';
            return hour + period;
        });
        filterPipsUsing((value, type) => {
            if (value === 0 || value === 12) return 1;
            if (value % 6 === 0) return 2;
            return 0;
        });
    "
/>

Star Rating Selector

Rate Your Experience

<x-ui.slider 
    wire:model="rating"
    :max-value="5"
    :step="0.5"
    pips
    pipsMode="steps"
    :pipsDensity="2"
    tooltips
    :fill-track="[true, false]"
    x-init="
        formatTooltipUsing((value) => '⭐'.repeat(Math.floor(value)) + (value % 1 ? '½' : ''));
        formatPipValueUsing((value) => value % 1 === 0 ? '⭐ ' + value : '');
        filterPipsUsing((value, type) => {
            return value % 1 === 0 ? 1 : 0;
        });
    "
/>

Temperature Range Control

Thermostat Control

❄️ Cool 😊 Comfortable 🔥 Warm
<x-ui.slider 
    wire:model="temperature"
    :min-value="50"
    :max-value="85"
    :step="1"
    pips
    pipsMode="steps"
    :pipsDensity="5"
    tooltips
    :fill-track="[true, false]"
    x-init="
        formatTooltipUsing((value) => value + '°F');
        formatPipValueUsing((value) => value + '°');
        filterPipsUsing((value, type) => {
            if (value % 10 === 0) return 1;
            if (value % 5 === 0) return 2;
            return 0;
        });
    "
/>
 <div class="mt-4 text-center">
    <span class="text-2xl font-bold" x-text="temperature + '°F'"></span>
    <div class="text-sm text-neutral-600 mt-1">
        <span x-show="temperature < 65">❄️ Cool</span>
        <span x-show="temperature >= 65 && temperature < 72">😊 Comfortable</span>
        <span x-show="temperature >= 72">🔥 Warm</span>
    </div>
</div>

Budget Allocation

Department Budget Allocation

Marketing
Development
Operations
Reserve
<x-ui.slider 
    wire:model="departmentBudgets"
    :max-value="10000"
    :step="100"
    pips
    pipsMode="steps"
    :pipsDensity="10"
    tooltips
    :fill-track="[true, true, true, false]"
    x-init="
        formatTooltipUsing((value) => '€' + (value / 1000).toFixed(1) + 'K');
        formatPipValueUsing((value) => '€' + (value / 1000) + 'K');
        filterPipsUsing((value, type) => {
            if (type === 0 && value < 1000) return -1;
            if (value % 2000 === 0) return 1;
            if (value % 1000 === 0) return 2;
            return 0;
        });
    "
/>
<div class="mt-6 grid grid-cols-4 gap-4 text-center text-sm">
    <div>
        <div class="font-semibold text-blue-600">Marketing</div>
        <div x-text="'€' + budgets[0]"></div>
    </div>
    <div>
        <div class="font-semibold text-green-600">Development</div>
        <div x-text="'€' + (budgets[1] - budgets[0])"></div>
    </div>
    <div>
        <div class="font-semibold text-purple-600">Operations</div>
        <div x-text="'€' + (budgets[2] - budgets[1])"></div>
    </div>
    <div>
        <div class="font-semibold text-neutral-600">Reserve</div>
        <div x-text="'€' + (10000 - budgets[2])"></div>
    </div>
</div>

Component Props

Prop Name Type Default Description
wire:model / x-model string - Bind to Livewire or Alpine state
name string - Input name attribute
min-value integer 0 Minimum value of the slider
max-value integer 100 Maximum value of the slider
step integer|null null Step increment (null = any decimal)
decimal-places integer|null null Number of decimal places to round to
range-padding integer|array null Behavioral padding at track edges
vertical boolean false Display slider vertically
top-to-bottom boolean false Reverse vertical slider direction
rtl boolean null Force right-to-left direction
fill-track array|boolean null Which track segments to fill with color
tooltips boolean false Show tooltips on handles
handle-variant string 'default' Handle style: default, circle
pips boolean false Enable pips (value markers)
pips-mode string|null null Pip mode: range, steps, positions, count, values
pips-density integer 10 Pip frequency (higher = fewer pips)
pips-values array|integer null Values/positions for pips (mode-dependent)
are-pips-stepped boolean false Round pips to steps (non-linear sliders)
margin integer|null null Minimum distance between handles
limit integer|null null Maximum distance between handles
behavior string 'tap' Interaction behavior: tap, drag, drag-fixed, none
non-linear-points array|null null Define non-linear track sections
disabled boolean false Disable user interaction
class string '' Additional CSS classes

JavaScript Callbacks

These methods are available via x-init to customize the slider's display:

formatTooltipUsing(callback)

Customize how tooltip values are displayed.

x-init="$slider.formatTooltipUsing((value) => ' + value.toFixed(2))"

Parameters:

  • value (number) - The current handle value

Returns: Formatted string to display in tooltip


formatPipValueUsing(callback)

Customize how pip labels are displayed.

x-init="formatPipValueUsing((value) => value + '%')"

Parameters:

  • value (number) - The pip value

Returns: Formatted string to display as pip label


filterPipsUsing(callback)

Control which pips are displayed and their size.

x-init="filterPipsUsing((value, type) => {
    if (value % 50 === 0) return 1;  // Large pip
    if (value % 10 === 0) return 2;  // Small pip
    return 0;                         // No label
})"

Parameters:

  • value (number) - The pip value
  • type (integer) - The pip type from noUiSlider (0, 1, or 2)

Returns:

  • 1 - Display large pip with label
  • 2 - Display small pip without label
  • 0 - Display pip without label
  • -1 - Hide pip completely

Combining Multiple Callbacks

You can chain multiple callbacks in x-init:

x-init="
    formatTooltipUsing((value) => ' + value.toFixed(2));
    formatPipValueUsing((value) => ' + value);
    filterPipsUsing((value, type) => {
        if (value % 100 === 0) return 1;
        if (value % 25 === 0) return 2;
        return 0;
    });
"

Accessibility

The slider component is built with accessibility in mind:

  • Keyboard navigation support (arrow keys to adjust values)
  • ARIA labels for screen readers
  • Proper focus management
  • Semantic HTML structure
  • High contrast mode compatible

Browser Support

The slider component works in all modern browsers that support:

  • ES6 JavaScript
  • CSS Grid and Flexbox
  • Alpine.js v3.x
  • Livewire v3.x (for Livewire integration)

Tips & Best Practices

When to Use Steps vs Decimal Places

  • Use step when you want discrete, predictable intervals (e.g., $10, $20, $30)
  • Use decimal-places when you want smooth movement with controlled precision (e.g., 3.142, 5.678)

Optimizing Pip Display

For sliders with large ranges, use pip filtering to avoid clutter:

<x-ui.slider 
    :max-value="1000"
    pips
    x-init="
        filterPipsUsing((value, type) => {
            if (value % 250 === 0) return 1;  // Major markers
            if (value % 50 === 0) return 2;   // Minor markers
            return -1;                         // Hide the rest
        });
    "
/>

Multiple Handles Best Practices

  • Always define fill-track with multiple handles for better UX
  • Use margin to prevent handles from overlapping
  • Use limit to enforce business rules (e.g., max budget range)
  • Provide clear labels/tooltips to distinguish handle purposes

Performance Considerations

  • Avoid overly dense pips (use pipsDensity wisely)
  • Keep tooltip formatting functions simple and fast
  • Use wire:model.live sparingly if you don't need real-time updates

© SheafUI Copyright 2024-2025. All rights reserved.