Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mutuadavi's avatar

Livewire v3 Range Slider

I have been trying different options to create a range slider in Livewire v3 in vain. I want a price slider which drags from both ends giving me the max and minimum values. Then I can use these values to query my Products table. Generally a filter. It's day 3 and haven't had any luck.

0 likes
2 replies
LaryAI's avatar
Level 58

Creating a range slider in Livewire that allows users to select a minimum and maximum value can be achieved by using a combination of Livewire's data binding and JavaScript interactivity. Here's a step-by-step solution to create a price slider that interacts with the Livewire component.

First, ensure you have Livewire installed and set up in your Laravel application.

  1. Create a Livewire component for the price slider.
php artisan make:livewire PriceSlider
  1. In your PriceSlider component (app/Http/Livewire/PriceSlider.php), define the properties for the minimum and maximum values.
namespace App\Http\Livewire;

use Livewire\Component;

class PriceSlider extends Component
{
    public $minPrice;
    public $maxPrice;

    public function mount($minPrice = 0, $maxPrice = 1000)
    {
        $this->minPrice = $minPrice;
        $this->maxPrice = $maxPrice;
    }

    public function render()
    {
        return view('livewire.price-slider');
    }
}
  1. Create the view for the component (resources/views/livewire/price-slider.blade.php). Use the wire:model directive to bind the slider values to the Livewire component properties.
<div>
    <input type="range" min="0" max="1000" value="{{ $minPrice }}" wire:model="minPrice" id="minPrice">
    <input type="range" min="0" max="1000" value="{{ $maxPrice }}" wire:model="maxPrice" id="maxPrice">
    <span>Min Price: {{ $minPrice }}</span>
    <span>Max Price: {{ $maxPrice }}</span>
</div>

@push('scripts')
<script>
    let minInput = document.getElementById('minPrice');
    let maxInput = document.getElementById('maxPrice');

    minInput.addEventListener('input', function () {
        if (parseInt(minInput.value) > parseInt(maxInput.value)) {
            minInput.value = maxInput.value;
        }
    });

    maxInput.addEventListener('input', function () {
        if (parseInt(maxInput.value) < parseInt(minInput.value)) {
            maxInput.value = minInput.value;
        }
    });
</script>
@endpush
  1. Include the Livewire scripts and styles in your main layout (resources/views/layouts/app.blade.php or similar).
<head>
    ...
    @livewireStyles
</head>
<body>
    ...
    @livewireScripts
</body>
  1. Use the Livewire component in your view where you want the price slider to appear.
<livewire:price-slider />
  1. Now, you can use the $minPrice and $maxPrice properties within your Livewire component to filter your Products table. For example, you might have a method that queries the products based on the selected price range:
public function updated($propertyName)
{
    $this->validateOnly($propertyName, [
        'minPrice' => 'numeric|min:0',
        'maxPrice' => 'numeric|max:1000',
    ]);

    // Query your products table with the new minPrice and maxPrice
    $products = Product::whereBetween('price', [$this->minPrice, $this->maxPrice])->get();

    // Do something with the products
}

Remember to replace the min and max values in the input range with the actual minimum and maximum values of your products' prices. Also, adjust the validation rules as needed for your specific use case.

This solution provides a basic range slider that updates the Livewire component's properties in real-time, which you can then use to filter your products.

MohamedTammam's avatar

This is a blade component that I created in a recent project for custom range slider using Livewire.

input-range.blade.php

@props(['min', 'max', 'id' => ''])
<div x-data="{
    progress: 0,
    v: 0,
    min: {{ $min }},
    max: {{ $max }},
    mouseDown: false,
    init() {
        this.v = this.$refs.input.value;
        wrapper = this.$refs.wrapper;
        this.progress = (this.v / this.max) * 100;
        this.$refs.input.addEventListener('input', () => {
            this.v = this.$refs.input.value;
        });

        setTimeout(() => this.fireInputEvent(), 500)

        this.$watch('v', (value) => {
            this.progress = (value / this.max) * 100;
        })

        wrapper.addEventListener('mousedown', (e) => {
            this.mouseDown = true;
            this.calculateMousePositionValue(e);
        });

        document.addEventListener('mouseup', () => {
            this.mouseDown = false;
        });

        document.addEventListener('mousemove', (e) => {
            if (this.mouseDown) {
                this.calculateMousePositionValue(e);
            }
        });
    },
    calculateMousePositionValue: function(e) {
        wrapper = this.$refs.wrapper;
        
        if(e.clientX < this.$refs.wrapper.offsetLeft) {
            this.$refs.input.value = this.min;
        } else if (e.clientX > this.$refs.wrapper.offsetLeft + this.$refs.wrapper.offsetWidth) {
            this.$refs.input.value = this.max;
        } else {
            let offsetFromWrapper = e.clientX - this.$refs.wrapper.offsetLeft;
            this.$refs.input.value = Math.max(Math.round((offsetFromWrapper / wrapper.offsetWidth) * this.max), this.min);
        }

        this.fireInputEvent();
    },
    fireInputEvent: function() {
        this.$refs.input.dispatchEvent(new Event('input', { bubbles: true }));
    }
}">
    {{-- Old input --}}
    {{-- We don't use its style now, But it should be there for native input elements events. --}}
    <input 
        {{ $attributes }}
        type="range" min="{{$min}}" max="{{$max}}"
        x-ref="input"
        class="!hidden"
        :title="v"
    >

    <div>
        <div x-ref="wrapper" class="relative select-none cursor-pointer">
            <div class="h-2 w-full rounded-full overflow-hidden bg-[#172C42]">
                <div class="bg-primary h-full relative" :style="{'width': progress + '%'}">
                </div>
            </div>
            <div 
                class="absolute top-1/2 -translate-y-1/2 -translate-x-1/2 flex items-center justify-center bg-primary tex-white px-1 min-w-[26px] h-[26px] border border-white rounded-[4px]"
                :style="{'left': progress + '%'}"
            >
                @isset($value)
                    {{ $value }}
                @else
                    <span x-text="v" class="text-sm font-medium"></span>
                @endif
            </div>
        </div>
    </div>
</div>

And that's how I use it

 <x-input-range :min="0" :max="50" wire:model.live="form.price">
		<x-slot:value>
        		<span class="text-white text-sm">
                		<span x-text="$wire.form.price"></span>&euro;
                </span>
		</x-slot:value>
</x-input-range>

Please or to participate in this conversation.