Skip to main content
The useRange hook provides the logic to build a custom component that allows users to filter results based on a numeric range.

Import

import { useRange } from 'react-instantsearch';

Parameters

attribute
string
required
Name of the attribute for faceting.
const { range, start, refine } = useRange({ attribute: 'price' });
min
number
Minimal range value (defaults to automatically computed from the result set).
const hook = useRange({ attribute: 'price', min: 0 });
max
number
Maximal range value (defaults to automatically computed from the result set).
const hook = useRange({ attribute: 'price', max: 1000 });
precision
number
default:"0"
Number of digits after decimal point to use.
const hook = useRange({ attribute: 'price', precision: 2 });

Returns

range
{ min: number, max: number }
Maximum range possible for this search.
const { range } = useRange({ attribute: 'price' });
console.log(range.min); // 0
console.log(range.max); // 999.99
start
[number | undefined, number | undefined]
Current refinement of the search (tuple of [min, max] bounds).
const { start } = useRange({ attribute: 'price' });
console.log(start); // [10, 100]
refine
(rangeValue: [number | undefined, number | undefined]) => void
Function to set a range to filter the results. Both values are optional and will default to the bounds.
const { refine } = useRange({ attribute: 'price' });
refine([10, 500]); // Filter between $10 and $500
refine([10, undefined]); // Filter $10 and above
refine([undefined, 500]); // Filter $500 and below
canRefine
boolean
Whether this widget can be refined.
const { canRefine } = useRange({ attribute: 'price' });
if (!canRefine) {
  return <p>No price range available</p>;
}
sendEvent
SendEventForFacet
Function to send Insights events.

Examples

Basic Range Input

import { useRange } from 'react-instantsearch';
import { useState } from 'react';

function PriceRange() {
  const { range, start, refine, canRefine } = useRange({
    attribute: 'price',
  });

  const [minValue, setMinValue] = useState(start[0] ?? range.min);
  const [maxValue, setMaxValue] = useState(start[1] ?? range.max);

  const handleSubmit = (e) => {
    e.preventDefault();
    refine([minValue, maxValue]);
  };

  if (!canRefine) {
    return null;
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Min:
        <input
          type="number"
          value={minValue}
          onChange={(e) => setMinValue(Number(e.target.value))}
          min={range.min}
          max={range.max}
        />
      </label>
      <label>
        Max:
        <input
          type="number"
          value={maxValue}
          onChange={(e) => setMaxValue(Number(e.target.value))}
          min={range.min}
          max={range.max}
        />
      </label>
      <button type="submit">Apply</button>
    </form>
  );
}

Range Slider

import { useRange } from 'react-instantsearch';
import { useState, useEffect } from 'react';

function PriceSlider() {
  const { range, start, refine, canRefine } = useRange({
    attribute: 'price',
  });

  const [values, setValues] = useState([start[0] ?? range.min, start[1] ?? range.max]);

  useEffect(() => {
    setValues([start[0] ?? range.min, start[1] ?? range.max]);
  }, [start, range]);

  const handleChange = (e, index) => {
    const newValues = [...values];
    newValues[index] = Number(e.target.value);
    setValues(newValues);
  };

  const handleChangeCommitted = () => {
    refine(values);
  };

  if (!canRefine) {
    return null;
  }

  return (
    <div>
      <h3>Price Range</h3>
      <div>
        <input
          type="range"
          min={range.min}
          max={range.max}
          value={values[0]}
          onChange={(e) => handleChange(e, 0)}
          onMouseUp={handleChangeCommitted}
          onTouchEnd={handleChangeCommitted}
        />
        <input
          type="range"
          min={range.min}
          max={range.max}
          value={values[1]}
          onChange={(e) => handleChange(e, 1)}
          onMouseUp={handleChangeCommitted}
          onTouchEnd={handleChangeCommitted}
        />
      </div>
      <div>
        ${values[0]} - ${values[1]}
      </div>
    </div>
  );
}

With Material-UI Slider

import { useRange } from 'react-instantsearch';
import Slider from '@mui/material/Slider';

function MUIRangeSlider() {
  const { range, start, refine, canRefine } = useRange({
    attribute: 'price',
  });

  const [value, setValue] = useState([
    start[0] ?? range.min,
    start[1] ?? range.max,
  ]);

  if (!canRefine) {
    return null;
  }

  return (
    <div style={{ padding: '0 20px' }}>
      <h3>Price Range</h3>
      <Slider
        value={value}
        onChange={(_, newValue) => setValue(newValue)}
        onChangeCommitted={(_, newValue) => refine(newValue)}
        valueLabelDisplay="auto"
        min={range.min}
        max={range.max}
        valueLabelFormat={(value) => `$${value}`}
      />
      <div>
        ${value[0]} - ${value[1]}
      </div>
    </div>
  );
}

Debounced Range Input

import { useRange } from 'react-instantsearch';
import { useState, useEffect, useRef } from 'react';

function DebouncedPriceRange() {
  const { range, start, refine } = useRange({ attribute: 'price' });
  const [min, setMin] = useState(start[0] ?? range.min);
  const [max, setMax] = useState(start[1] ?? range.max);
  const timerRef = useRef(null);

  useEffect(() => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
    }
    timerRef.current = setTimeout(() => {
      refine([min, max]);
    }, 500);

    return () => {
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }
    };
  }, [min, max, refine]);

  return (
    <div>
      <input
        type="number"
        value={min}
        onChange={(e) => setMin(Number(e.target.value))}
        placeholder="Min"
      />
      <input
        type="number"
        value={max}
        onChange={(e) => setMax(Number(e.target.value))}
        placeholder="Max"
      />
    </div>
  );
}

TypeScript

import { useRange } from 'react-instantsearch';
import type { UseRangeProps } from 'react-instantsearch';

function PriceRange(props: UseRangeProps) {
  const { range, start, refine } = useRange(props);

  return (
    <div>
      <input
        type="number"
        value={start[0] ?? range.min}
        onChange={(e) => refine([Number(e.target.value), start[1]])}
      />
      <input
        type="number"
        value={start[1] ?? range.max}
        onChange={(e) => refine([start[0], Number(e.target.value)])}
      />
    </div>
  );
}