react-slick icon indicating copy to clipboard operation
react-slick copied to clipboard

responsive breakpoints are not working

Open Vijaykumarparelli opened this issue 2 months ago • 9 comments

Hey @react-slick

"dependencies": { "next": "16.0.1", "react": "19.2.0", "react-dom": "19.2.0", "react-slick": "^0.31.0", "slick-carousel": "^1.8.1" }, "devDependencies": { "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "@types/react-slick": "^0.23.13", "eslint": "^9", "eslint-config-next": "16.0.1", "tailwindcss": "^4", "typescript": "^5" } this is my package json dependencies and devDependencies

'use client'; import Slider from 'react-slick'; import 'slick-carousel/slick/slick.css'; export default function Home() { var settings = { dots: true, infinite: false, speed: 500, slidesToShow: 4, slidesToScroll: 4, initialSlide: 0, responsive: [ { breakpoint: 1024, settings: { slidesToShow: 3, slidesToScroll: 3, infinite: true, dots: true, }, }, { breakpoint: 600, settings: { slidesToShow: 2, slidesToScroll: 2, initialSlide: 2, }, }, { breakpoint: 480, settings: { slidesToShow: 1, slidesToScroll: 1, }, }, ], }; return ( <div className=""> <div className="slider-container"> <Slider {...settings}>

<h3 className='bg-amber-950 text-amber-300'>1
<h3 className='bg-amber-950 text-amber-300'>2
<h3 className='bg-amber-950 text-amber-300'>3
<h3 className='bg-amber-950 text-amber-300'>4
<h3 className='bg-amber-950 text-amber-300'>5
<h3 className='bg-amber-950 text-amber-300'>6
<h3 className='bg-amber-950 text-amber-300'>7
<h3 className='bg-amber-950 text-amber-300'>8
</Slider> ); } this is sample slider which i copied from documentation

issue is breakpoint settings are not woking in responsive

Vijaykumarparelli avatar Nov 11 '25 14:11 Vijaykumarparelli

Yes same issue I am facing

deepak-almabetter avatar Nov 13 '25 11:11 deepak-almabetter

It seems to be an issue with the latest version, 0.31.0. When installing the 0.30.3 version, it works as intended.

tutagomes avatar Nov 16 '25 00:11 tutagomes

I am running into this issue as well. From what I can tell, for me the issue is the react-slick Slider component is only checking the current windows width on window resize, but not on initial render. So instead of using the appropriate settings set via the responsive prop on render, it will instead use the defaults UNTIL you resize the window AND the resize hits a breakpoint defined in the responsive prop.

John-Youngblood avatar Nov 20 '25 22:11 John-Youngblood

I was able to reproduce this behavior in the responsive playwright test. Here are the settings being used by the responsive tests: https://github.com/akiran/react-slick/blob/97442318e9a442bd4a84eb25133ef62087f98232/playwright-tests/features/responsive/responsive.story.tsx#L5-L38

In the test spec responsive.spec.tsx, If you change the initial viewport from 1200x500 to 1020x500, you would expect the first test to fail since 1000 width is within the 3 active slides breakpoint:

  await expect(await activeSlidesCount(component)).toEqual(4);

however its the second test that fails:

  await page.setViewportSize({ width: 1000, height: 500 });
  await page.waitForTimeout(100);
  await expect(await activeSlidesCount(component)).toEqual(3);

since even though the viewport changed, it didn't pass a breakpoint, so react-slider did not update the settings based on the responsive attribute.

John-Youngblood avatar Nov 20 '25 22:11 John-Youngblood

Found the problematic code (it stems from react-slider moving away from using enquire.js and using window.watchMedia instead). I pushed a PR that I believe fixes this issue, so hopefully it will get merged soon. Until then, downgrading to version 0.30.3 like @tutagomes mentioned is the current workaround.

John-Youngblood avatar Nov 20 '25 22:11 John-Youngblood

I've solved the problem by make custom hook that act like the responsive property in the library

import { useEffect, useState } from "react";

/**
 * React hook to run callbacks when the window width crosses specified breakpoints.
 *
 * @typedef {Object} UseMediaQueryParam
 * @property {number} breakpoint - Breakpoint width in pixels.
 * @property {function(): void} onChange - Callback invoked when the viewport crosses the breakpoint.
 *
 * @param {UseMediaQueryParam[]} breakpoints - Array of breakpoint definitions.
 * @returns {void}
 */
export function useMediaQuery(breakpoints) {
	const [innerWidth, setInnerWidth] = useState(typeof window !== "undefined" ? window.innerWidth : 0);
	useEffect(() => {
		function handleResize() {
			setInnerWidth(window.innerWidth);
		}
		handleResize();
		window.addEventListener("resize", handleResize);
		return () => {
			window.removeEventListener("resize", handleResize);
		};
	}, []);
	useEffect(() => {
		breakpoints.forEach(({ breakpoint, onChange }) => {
			if (innerWidth <= breakpoint) {
				onChange();
			}
		});
	}, [innerWidth]);
}

first wrap the carousel settings in state, then use the hook like this

useMediaQuery([
	{
		breakpoint: Infinity,
		onChange: () => {
			setCarouselSettings((prev) => ({
				...prev,
				slidesToShow: 4,
				slidesToScroll: 2,
			}));
		},
	},
        ...
])

note: i write the code in JS, because my team it still using JS :)

Eathen0 avatar Nov 30 '25 02:11 Eathen0

The breakpoints were working for me when I resized the window, but the initial load wasn't. Not sure why, but what fixed this for me was sorting the breakpoints array myself.

My hunch is telling me, some calculation is happening when the component mounts but not when the number of children changes. I start with an empty array, then a loading item one, the the real data once it's done fetching.

ysabri avatar Nov 30 '25 18:11 ysabri

here is the updated typescript version of the proposed useMediaQuery fix been proposed above

useMediaQuery hook


import { useEffect, useState } from "react";

export interface UseMediaQueryParam {
  breakpoint: number;
  onChange: () => void;
}

/**
 * React hook to run callbacks when the window width crosses specified breakpoints.
 */
export function useMediaQuery(breakpoints: UseMediaQueryParam[]): void {
  const [innerWidth, setInnerWidth] = useState(
    typeof window !== "undefined" ? window.innerWidth : 0
  );

  useEffect(() => {
    const handleResize = () => {
      setInnerWidth(window.innerWidth);
    };

    handleResize();
    window.addEventListener("resize", handleResize);

    return () => window.removeEventListener("resize", handleResize);
  }, []);

  useEffect(() => {
    const sorted = [...breakpoints].sort((a, b) => a.breakpoint - b.breakpoint);

    const match = sorted.find((bp) => innerWidth <= bp.breakpoint);

    if (match) {
      match.onChange();
    }
  }, [innerWidth, breakpoints]);
}

and here is its usage

useSliderSettings hook

import { useState } from "react";
import { useMediaQuery } from "./useMediaQuery"; // import your hook

export function useSliderSettings() {
  const [slidesToShow, setSlidesToShow] = useState(4);
  const [currentSlide, setCurrentSlide] = useState(0);

  useMediaQuery([
    {
      breakpoint: 500,
      onChange: () => {
        setSlidesToShow(1);
      },
    },
    {
      breakpoint: 900,
      onChange: () => setSlidesToShow(2),
    },
    {
      breakpoint: 1280,
      onChange: () => setSlidesToShow(3),
    },
    {
      breakpoint: 1560,
      onChange: () => setSlidesToShow(3),
    },
    {
      breakpoint: 3600,
      onChange: () => setSlidesToShow(4),
    },
  ]);

  return {
    dots: false,
    infinite: false,
    speed: 500,
    slidesToShow,
    slidesToScroll: 1,
    arrows: false,
    swipeToSlide: true,
    afterChange: (index: number) => setCurrentSlide(index),
    currentSlide,
  };
}

and it can now be called within your component level

const settings = useSliderSettings();

jettechnologies avatar Dec 03 '25 12:12 jettechnologies

I've solved the problem by make custom hook that act like the responsive property in the library

import { useEffect, useState } from "react";

/**
 * React hook to run callbacks when the window width crosses specified breakpoints.
 *
 * @typedef {Object} UseMediaQueryParam
 * @property {number} breakpoint - Breakpoint width in pixels.
 * @property {function(): void} onChange - Callback invoked when the viewport crosses the breakpoint.
 *
 * @param {UseMediaQueryParam[]} breakpoints - Array of breakpoint definitions.
 * @returns {void}
 */
export function useMediaQuery(breakpoints) {
	const [innerWidth, setInnerWidth] = useState(typeof window !== "undefined" ? window.innerWidth : 0);
	useEffect(() => {
		function handleResize() {
			setInnerWidth(window.innerWidth);
		}
		handleResize();
		window.addEventListener("resize", handleResize);
		return () => {
			window.removeEventListener("resize", handleResize);
		};
	}, []);
	useEffect(() => {
		breakpoints.forEach(({ breakpoint, onChange }) => {
			if (innerWidth <= breakpoint) {
				onChange();
			}
		});
	}, [innerWidth]);
}

first wrap the carousel settings in state, then use the hook like this

useMediaQuery([
	{
		breakpoint: Infinity,
		onChange: () => {
			setCarouselSettings((prev) => ({
				...prev,
				slidesToShow: 4,
				slidesToScroll: 2,
			}));
		},
	},
        ...
])

note: i write the code in JS, because my team it still using JS :)

thanks for this code snippet

jettechnologies avatar Dec 03 '25 12:12 jettechnologies