Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Livewire Range Slider Laravel Package

jantinnerezo/livewire-range-slider

jantinnerezo/livewire-range-slider lets you easily add a configurable range slider to Livewire forms in Laravel. This lightweight Blade component supports single or dual handles, two-way model binding, min/max values, step sizes, and custom styling, with no extra JavaScript setup required.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm compatibility first.

composer require jantinnerezo/livewire-range-slider

Before writing any markup, open vendor/jantinnerezo/livewire-range-slider/composer.json and check the require block. The last tagged release was June 2022, which predates Livewire 3 — if your app is on Livewire 3, treat this package as "evaluate, don't assume." On Livewire 2 it should drop in cleanly.

Check what the service provider does. The package is auto-discovered, but confirm whether it registers a config file, a view namespace, or a public asset. Look for a publishes() call in the provider:

php artisan vendor:publish --provider="Jantinnerezo\LivewireRangeSlider\LivewireRangeSliderServiceProvider"

Where to look first (in this order):

  1. src/ — the Livewire component class. This tells you the exact public property name the slider binds to and whether the value is a scalar or an array.
  2. resources/views/ — the Blade template. Confirms prop names for min, max, step, and tooltip formatting.
  3. resources/js/ — the "no external JS" implementation. Verifies it's vanilla and how it dispatches updates back to Livewire.

First use case — a price filter with two handles.

The dual-handle design almost always means the bound property is an array [min, max]. Declare it as such in your parent component:

// app/Http/Livewire/ProductSearch.php
class ProductSearch extends Component
{
    public array $price = [0, 1000];

    public function render()
    {
        return view('livewire.product-search', [
            'products' => Product::whereBetween('price', $this->price)->get(),
        ]);
    }
}
{{-- resources/views/livewire/product-search.blade.php --}}
<div>
    <livewire:range-slider wire:model.live="price" :min="0" :max="1000" :step="10" />

    <p>Showing ${{ $price[0] }} – ${{ $price[1] }}</p>

    @foreach ($products as $product)
        <div>{{ $product->name }}</div>
    @endforeach
</div>

If the property shape doesn't match what the component emits (scalar vs array), Livewire will throw a TypeError on the first update — the source inspection above prevents this.


Implementation Patterns

Choose your binding strategy deliberately.

Binding When to use
wire:model.live Live filtering / instant results
wire:model.live.debounce.500ms Live filtering where each drag tick would otherwise fire a request
wire:model (default, deferred) Slider is part of a form submitted via a button

For a range slider, deferred binding is a common trap: the handles move visually, but the server only learns about it on the next request. If your preview panel doesn't update, this is why.

Debounce aggressively — drag events are chatty.

<livewire:range-slider
    wire:model.live.debounce.500ms="price"
    :min="0"
    :max="1000"
    :step="50"
/>

Without debouncing, a single drag can produce dozens of round-trips and visibly stall the UI.

Keep filter state in the URL for shareable results.

protected $queryString = [
    'price' => ['except' => [0, 1000]],
];

Note that query-string values arrive as strings; cast explicitly in mount() or a updatedPrice() hook if your comparisons are strict.

Reset pattern. A "Clear filters" button just reassigns the property:

<button wire:click="$set('price', [0, 1000])">Reset</button>

Reuse via a wrapper Blade component. Rather than repeating the tag with the same props everywhere, wrap it:

{{-- resources/views/components/price-filter.blade.php --}}
@props(['model' => 'price', 'max' => 1000])
<livewire:range-slider
    wire:model.live.debounce.500ms="{{ $model }}"
    :min="0"
    :max="$max"
    :step="10"
/>

Multiple sliders on one page. Give each a stable wire:key on the parent loop item; Livewire's DOM diffing can otherwise preserve the wrong slider state:

@foreach ($categories as $category)
    <div wire:key="slider-{{ $category->id }}">
        <livewire:range-slider wire:model.live="ranges.{{ $category->id }}" />
    </div>
@endforeach

Validation belongs on the parent. The slider can emit values outside your business rules (e.g. min > max in edge cases), so validate in the parent's updatedPrice():

public function updatedPrice($value)
{
    $this->validate(['price' => 'array|size:2', 'price.*' => 'numeric|min:0|max:1000']);
}

Tooltip formatting. The description advertises "formatted tooltips." Check the source for whether the prop is a format string, a closure, or a Blade slot — closures generally do not survive Livewire serialization, so if it's a closure-based formatter you'll need to pass a string pattern or precompute display text in the parent.

Integration with a filter form. Combine with selects/checkboxes; on each updated hook, re-query. Keep the query in a buildQuery() private method so the render stays readable as filters grow.


Gotchas and Tips

Livewire 3 compatibility is the big unknown. Last release 2022-06-11. Livewire 3 changed component naming, wire:model semantics, and asset injection. Before committing:

  • Smoke-test the component in a throwaway Livewire 3 route.
  • Verify the JS uses Livewire.find(...) / $wire in a way that survives the v3 refactor (v2-era packages often call window.livewire.emit or rely on wire:model internals).
  • If it breaks, the source is small enough to fork or copy the view + JS into your app as a local component — MIT license permits this.

Property type must match exactly. If the source emits [min, max] and you declared public $price = 500, Livewire throws on the first update. Conversely, public array $price = [0, 1000] fails if the component emits a scalar string. Read the component class before wiring it up.

wire:model vs wire:model.live is the #1 debugging trap with this package. Symptom: handle moves, tooltip updates, but the server-side value or preview doesn't change. Fix: add .live (plus .debounce).

Numeric strings from query strings. After a page reload with ?price[0]=100&price[1]=500, values are strings. whereBetween usually tolerates this, but strict === comparisons and some validators will not. Cast in mount():

public function mount()
{
    $this->price = array_map('intval', (array) $this->price);
}

Prop reactivity. :min/:max/:step are likely read once at component mount. If you need them dynamic (e.g. dependent on another filter), test whether they update — otherwise add wire:key to force a remount:

<livewire:range-slider
    wire:key="slider-{{ $selectedCategory }}"
    wire:model.live="price"
    :min="0"
    :max="$categoryMax"
/>

Assets. "No external JS" does not mean "no JS" — it means no jQuery/plugin dependency. Ensure @livewireScripts is present (v2) or that Livewire 3's auto-injection hasn't been disabled. If you use @livewireScripts(config: ['inject_assets' => false]), the slider will render but stay inert.

Styling overrides. Publish the views before customizing so you can see the class names:

php artisan vendor:publish --tag=livewire-range-slider-views

Remember that published views shadow the package on upgrade — diff them when you bump the (stale) version.

Debugging sync issues. In the browser devtools Network tab, inspect the Livewire update payload. If price isn't in updates, the JS never called $wire.set. If it is but the render didn't change, your render() is reading a stale or differently-shaped property. A quick ray($this->price) (or logger($this->price)) inside updatedPrice() confirms what the server actually receives.

Accessibility. Custom slider markup predating 2023 usually lacks role="slider", aria-valuemin/max/now, and keyboard support. If this is a user-facing filter, wrap it with an associated <label> and consider adding ARIA attributes in the published view.

Performance at scale. Use :step to reduce event frequency, and prefer .debounce.500ms over raw .live when the parent re-queries the database. For large result sets, combine with pagination so each update isn't a full table scan.

Extension points are limited. There's no plugin API documented; the practical extension mechanism is (a) publish and edit the view, or (b) copy the component into app/Livewire/ and own it. Given the package's age and low activity, option (b) is often the lower-risk choice for a production app.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource