150 €750 €
React + MotionChamps

Curseur de fourchette de prix

Deux curseurs pour le prix minimum et maximum, avec un petit histogramme qui montre où se concentrent les prix.

Réglages

150€
750€
1

Code

import { useState } from "react";
import { motion } from "framer-motion";

const spring = { type: "spring" as const, stiffness: 300, damping: 30 };

const bars = [5, 8, 13, 20, 28, 35, 42, 50, 46, 38, 31, 23, 16, 10];

const rangeClass = [
  "h-1 w-full appearance-none rounded-full bg-black/5 outline-none dark:bg-white/10",
  "[&::-webkit-slider-thumb]:h-[22px] [&::-webkit-slider-thumb]:w-[22px] [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-[0_1px_4px_rgba(0,0,0,0.25),0_0_0_0.5px_rgba(0,0,0,0.06)]",
  "[&::-moz-range-thumb]:h-[22px] [&::-moz-range-thumb]:w-[22px] [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-none [&::-moz-range-thumb]:bg-white [&::-moz-range-thumb]:shadow-[0_1px_4px_rgba(0,0,0,0.25)]",
].join(" ");

interface PriceRangeProps {
  value?: [number, number];
  defaultValue?: [number, number];
  onChange?: (value: [number, number]) => void;
  height?: number;
}

export default function PriceRange({
  value,
  defaultValue = [150, 750],
  onChange,
  height = 1,
}: PriceRangeProps) {
  const [internal, setInternal] = useState<[number, number]>(defaultValue);
  const [a, b] = value ?? internal;

  const set = (next: [number, number]) => {
    if (value === undefined) setInternal(next);
    onChange?.(next);
  };

  return (
    <div className="w-72 rounded-[22px] bg-white p-5 text-[#1d1d1f] shadow-[0_1px_2px_rgba(0,0,0,0.03),0_8px_28px_rgba(0,0,0,0.05)] dark:bg-[#1c1c1e] dark:text-[#f5f5f7]">
      <div className="mb-3 flex h-16 items-end gap-1">
        {bars.map((h, i) => (
          <motion.i
            key={i}
            className="flex-1 rounded-t bg-[#0071e3]/30"
            animate={{ height: h * height }}
            transition={{ ...spring, delay: i * 0.02 }}
          />
        ))}
      </div>
      <div className="space-y-2">
        <input
          aria-label="Prix minimum"
          className={rangeClass}
          type="range"
          min="0"
          max="1000"
          value={a}
          onChange={(e) => set([Math.min(Number(e.target.value), b - 25), b])}
        />
        <input
          aria-label="Prix maximum"
          className={rangeClass}
          type="range"
          min="0"
          max="1000"
          value={b}
          onChange={(e) => set([a, Math.max(Number(e.target.value), a + 25)])}
        />
      </div>
      <div className="mt-3 flex justify-between text-sm">
        <span>{a} €</span>
        <span>{b} €</span>
      </div>
    </div>
  );
}

Prompt pour l’IA

Collez-le dans ChatGPT, Claude ou Cursor : le bloc sera adapté à votre projet avec ces réglages, même sans React.

Plus dans Champs

Où l’utiliser

Un filtre de prix avec histogramme montre où se trouvent la plupart des produits avant même de toucher au curseur. On le voit sur les sites de location de vacances, d’annonces auto et les grandes boutiques. Les deux curseurs ne se croisent jamais. Si votre catalogue a une colonne de filtres, ce slider de fourchette de prix s’y glisse naturellement.

Les barres de la démo sont des données d’exemple : alimentez-les avec la vraie répartition de vos prix, sinon elles induiront en erreur. Ajustez la hauteur de l’histogramme à votre colonne latérale et changez le symbole monétaire si vous ne vendez pas en euros.