Create a React Multi Range Slider with two Handles using react-slider and TailwindCSS
Jasser Mark Arioste
Hello, hustlers! In this tutorial, you'll learn how to create a react multi-range slider step-by-step by using the react-slider
package. We'll also use Tailwind CSS classes for the styling and the classnames
package to control the styles. Also, I use Typescript for all my projects so we'll be using it as well.
What is the react-slider
package?
#
The react-slider package is a very small (3.9kB min+gzipped) and versatile component library to implement range sliders. It is completely unstyled, so it's up to you to implement the styling depending on the component state. We can implement regular, multi-range sliders with vertical or horizontal orientations.
It also has a minimal footprint (only 3.9kB) and no dependencies.
Why use TailwindCSS? #
I really like to use TailwindCSS since it's very flexible a set of CSS utility classes. It's very easy to integrate CSS utility classes into any component library in React.
Final Output #
Here's the final output of the react multi-range slider that we'll be making today.
Things to Do #
- Project setup. We'll set up a NextJS Typescript project + TailwindCSS Setup for this tutorial.
- Create a wrapper component for
ReactSlider
that uses typescript and our styles. - Style the slider Thumb using TailwindCSS. The Thumb is the handle that has the current slider value.
- Style the slider Tracks. Tracks are the light gray and indigo lines you see above. We must consider that sometimes there are 2 values (for multi-range). We'll have to style it accordingly.
- Style the slider Marks/Steps. The marks/steps are the tiny dots representing each slider step.
- Style the Thumb according to horizontal/vertical orientation.
Step 0 - Project Setup #
If you already have Tailwind CSS installed on your project, you may skip this step. If you're using a different framework other than NextJS, please check tailwind CSS installation docs.
To follow along on this tutorial, I created a NextJS starter template in GitHub that has Tailwind CSS pre-installed so we'll just use that to bootstrap our project. Run the following command:
npx create-next-app -e https://github.com/jmarioste/react-multi-range-slider-tutorial react-multi-range-slider
Next, run the following command to start the local server:
cd react-multi-range-slider && yarn dev
Go to localhost:3000 and you'll be greeted by this screen:
Step 1 - Installing the Dependencies #
Now that the setup is done, let's install all the dependencies we'll be using. Run the following command:
yarn add react-slider classnames && yarn add -D @types/react-slider
Step 2 - Creating the Wrapper Component #
Next, let's create a wrapper component called RangeSlider. Create the file components/RangeSlider.ts and copy the code below:
// components/RangeSlider.tsx import React from "react"; import ReactSlider, { ReactSliderProps } from "react-slider"; const RangeSlider = <T extends number | readonly number[]>(_props: ReactSliderProps<T>) => { return <ReactSlider {..._props} className="w-full h-full" />; }; export default RangeSlider;
1234567
Explanation:
Line 3-4: To properly create a wrapper component, we use the ReactSliderProps so that we can just use whatever props it already has. But since ReactSliderProps
has a generic parameter <T>
that accepts number
or number[]
, we have to convert our component into a generic component as well.
If we don't use a generic component, you'll get an error when assigning a value of type number[]
in values
or defaultValues
prop. For example, let's say we define our component like this:
// components/ReactSliderNonGeneric.tsx import React from "react"; import ReactSlider, { ReactSliderProps } from "react-slider"; const RangeSliderNonGeneric = (_props: ReactSliderProps) => { return <ReactSlider {..._props} className="w-full h-full" />; }; export default RangeSliderNonGeneric;
1234567
We'll get a compile-time error: Type 'number[]' is not assignable to type 'number'.
when using the component.
Step 3 - Styling the Thumb Component #
The next step is to style the Thumb component. We'll use the renderThumb
prop to fully customize how we render our thumb component. The renderThumb
prop is a custom render function for dynamic thumb content:
// components/RangeSlider.tsx import React from "react"; import ReactSlider, { ReactSliderProps } from "react-slider"; import cn from "classnames"; const RangeSlider = <T extends number | readonly number[]>( _props: ReactSliderProps<T> ) => { const isVertical = _props.orientation === "vertical"; return ( <ReactSlider {..._props} renderThumb={(props, state) => ( <div {...props} className={cn({ "h-full": !isVertical, "w-full": isVertical, "aspect-square rounded-full bg-indigo-500 text-xs text-white flex items-center justify-center cursor-grab": true, })} > {state.valueNow} </div> )} /> ); }; export default RangeSlider;
12345678910111213141516171819202122232425262728
Explanation:
Line 4: We import classnames
to easily control the classes
Line 8: We check if the orientation is vertical or horizontal.
Line 13-25: We implement the thumb content. We consider the orientation and adjust the styling accordingly. We use apsect-square
and rounded-full
classes to make sure it's a circle depending on the height or width of the slider.
To use it we can just add classes to define the dimensions:
import type { NextPage } from "next"; import RangeSlider from "../components/RangeSlider"; const Home: NextPage = () => { return ( <div className="flex flex-col gap-2 p-4"> <h1 className="text-3xl font-bold underline">Hello world!</h1> <RangeSlider className="w-80 h-12" defaultValue={50} /> <RangeSlider className="w-40 h-8" defaultValue={[25, 75]} /> <RangeSlider className="w-8 h-40" orientation="vertical" defaultValue={50} /> </div> ); }; export default Home;
1234567891011121314151617
After this step, we can already play with our range slider a bit:
Step 4 - Styling the Tracks #
The next step is to style the tracks. There will be more logic in this step since we have to consider the number of values as well as the orientation. We'll use the renderTrack
prop which is similar to the renderThumb
prop.
// components/RangeSlider.tsx import React from "react"; import ReactSlider, { ReactSliderProps } from "react-slider"; import cn from "classnames"; const RangeSlider = <T extends number | readonly number[]>( _props: ReactSliderProps<T> ) => { const isVertical = _props.orientation === "vertical"; return ( <ReactSlider {..._props} ... renderTrack={(props, state) => { //check if there are multiple values const points = Array.isArray(state.value) ? state.value.length : null; const isMulti = points && points > 0; const isLast = isMulti ? state.index === points : state.index === 1; const isFirst = state.index === 0; return ( <div {...props} className={cn({ //use 1/4 height or width depending on the orientation and make sure to center it. "h-1/4 top-1/2 -translate-y-1/2": !isVertical, "w-1/4 left-1/2 -translate-x-1/2": isVertical, "rounded-full": true, "bg-gray-200": isMulti ? isFirst || isLast : isLast, "bg-indigo-500": isMulti ? !isFirst || !isLast : isFirst, })} ></div> ); }} /> ); }; export default RangeSlider;
123456789101112131415161718192021222324252627282930313233343536
Explanation:
react-slider
provides state.index
the position of the track relative to the value. We change the background color depending on the index
/position of the track.
After this step, here's the output:
It already looks good and usable.
Step 4 (Optional) - Styling the Steps/Marks #
This step is optional if you don't want to style it. We'll use the renderMark
prop similar to the previous two render functions:
// components/RangeSlider.tsx import React from "react"; import ReactSlider, { ReactSliderProps } from "react-slider"; import cn from "classnames"; const RangeSlider = <T extends number | readonly number[]>( _props: ReactSliderProps<T> ) => { const isVertical = _props.orientation === "vertical"; return ( <ReactSlider {..._props} ... renderMark={(props) => { return ( <div {...props} className={cn({ "top-1/2 -translate-y-1/2": !isVertical, "left-1/2 -translate-x-1/2": isVertical, "w-1/5 h-1/5": true, "rounded-full bg-indigo-500": true, })} ></div> ); }} /> ); }; export default RangeSlider;
1234567891011121314151617181920212223242526272829
Usage:
// pages/index.tsx import type { NextPage } from "next"; import RangeSlider from "../components/RangeSlider"; const Home: NextPage = () => { return ( <div className="flex flex-col gap-2 p-4"> <RangeSlider className="w-80 h-12" defaultValue={50} marks={10} step={10} /> <RangeSlider className="w-40 h-8" defaultValue={[25, 75]} /> <RangeSlider className="w-8 h-40" orientation="vertical" defaultValue={50} /> </div> ); }; export default Home;
12345678910111213141516171819202122
That's it!
Full Code and Demo #
The full code is available at GitHub step-by-step-tutorial branch.
The demo is available on Stackblitz: React Multi-Range Slider Demo
Conclusion #
We learned how to create a react multi-range slider by using the awesome package react-slider that already has this functionality and we only needed to add the styling. We used Tailwind CSS to easily style the individual components of the slider.
If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter.
Credits: Image by Alex Hu from Pixabay