Image Slider with React Slick
React slick is a great carousel component for creating an image slider or carousel-like UI components. But sometimes we need some manual control over the default features that the library provides.
Last time when I worked with the library I tried to create an image slider with some extra features that React Slick doesn’t provide. I need to create a slider where I will conditionally show each slider navigator on the left side and right side.
But unfortunately, there were no such features in React Slick to conditionally show just one arrow navigator.
Today I am going to share my experience of customizing the React Slick component with custom arrow navigators.
Installation
Installing React Slick
yarn add react-slick
I am using typescript for the demonstration so I need to add type definition.
yarn add @types/react-slick
More info about the React Slick
I am also using Emotion for styling the components but that is totally up to you.
Usage
Initial Image Slider component
Sider Arrow component
For simplicity, I will add the React Slick styles directly in Style.css
// Style.css
@import url('https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css');@import url('https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css');
This will render a simple image slider.
Click Events of Slider Arrow
Now we have to control the onClick
events of the SliderArrow. The functionality will be going to the next or previous image by clicking those SliderArrow.
If we go to the definition of Slider
component (By Command + Click) that we are using in the ImageSlider component then we will see that it extends a React component with some additional methods.
We need to access the slickNext
and slickPrev
methods.
To access those methods we will use useRef
hooks from react. We will set a reference in the Slider
component and then we will be able to access all the public properties and methods from Slider component by using that reference.
const sliderRef = useRef<Slider>(null);
..........
return (
<SliderWrapper>
<SliderArrow type="prev" onClick={() => sliderRef.current.slickPrev()} />}
.................
<SliderArrow type="next" onClick={() => sliderRef.current.slickNext()} />}
</SliderWrapper>
);
Now if you click on the Slider Arrow’s then you can traverse the images using those two buttons.
Advanced Features
Now it’s time to make the slider more intelligent.
If we are are at the beginning of the slider then there is no point to show the previous arrow and if we are at the end of the slider then we should also not show the next arrow.
To control the visibility we need to know the current slide position. We can get that by afterChange
option from Slider settings object.
const settings: Settings = {
.......,
afterChange: (currentSlide: number) => handleChangeSlide(currentSlide)
}
We will also declare two states for controlling the visibility of arrow buttons. One more state to set the max number of slides to show.
const [showRightArrow, setShowRightArrow] = useState<boolean>(false);
const [showLeftArrow, setShowLeftArrow] = useState<boolean>(false);const [maxNumberOfCardsToShow, setMaxNumberOfCardsToShow] = useState<number>(0);
The change slide handler
const handleChangeSlide = (currentSlide: number) => {
const leftArrowVisible = currentSlide !== 0;
const rightArrowVisible = currentSlide <= data.length - maxNumberOfCardsToShow;setShowLeftArrow(leftArrowVisible);
setShowRightArrow(rightArrowVisible);};
The above handler code will be executed after each slide change.
Now we will conditionally render the arrow buttons.
{showLeftArrow && ( <SliderArrow type="prev" onClick={() => sliderRef.current.slickPrev()} />)}
........
{showRightArrow && ( <SliderArrow type="next" onClick={() => sliderRef.current.slickNext()} />)}
Now when the component rendered first we have to check the maximum number of images that we can show in one frame and also we have to check whether we will show the right arrow or not.
So we will set another div referenceSliderWrapper
to know the width of the wrapper.
const sliderWrapperRef = useRef<HTMLDivElement>(null);...<SliderWrapper ref={sliderWrapperRef}>
We will perform all the calculations inside useEffect
hook.
useEffect(() => {
const wrapperWidth = sliderWrapperRef.current.clientWidth || 0;
const maxNumberOfCards = Math.ceil(wrapperWidth / EACH_SLIDE_WIDTH);
setMaxNumberOfCardsToShow(maxNumberOfCards); setShowRightArrow(data.length > maxNumberOfCards);
}, []);
I declared the EACH_SLIDE_WIDTH
as a constant on the top of the component. You can also get this value as props.
const EACH_SLIDE_WIDTH = 176;
That’s all! Now you have a full-featured image slider with all of the controls on your hand.
I have done some more refactoring and style improvements. Here is the link to the complete code.