How to make number of visibleSlides responsive in CSS in pure-react-carousel

3.7k Views Asked by At

How/can we make number of visibleSlides responsive in CSS? / not have to use JS to alter visibleSlides based on breakpoints.

For example; Each slide has min-width: 100px; min-height: 100px; ie. image we want to see detail so shouldn't be smaller than 100px.

We set visibleSlides to 8 (for desktop). On mobile we want to show only 2 slides. Because we don't want the individual slides to be less than 100px height and width, nor have the slides overlapping.

I know we could use react to check screen width and set visibleSlides, however it's not easy for all apps to have access to this, especially server side rendered like next.js.

See this sandbox https://codesandbox.io/s/pure-react-carousel-responsive-visible-slides-k8cui

(Forked from https://codesandbox.io/s/withered-wood-4bx36?fontsize=14&hidenavigation=1&theme=dark)

3

There are 3 best solutions below

0
On

not an answer, but i'm trying to do the same. How about using React hooks useState for the visibleSlides integer, and creating a window listener that listens for breakpoints and changes the state as needed....

1
On

I had that problem for my app and actually couldn't find a way to do this with CSS. However, I've implemented this using ResizeObserver and react-hooks.
P.S. I'm using next js, and it's not a big issue to implement it on the server-side.
Here's my solution, hope it could help.
Step 1. Create an observer hook to listen for resize events from the app.

import { useEffect, useState, RefObject } from 'react';
import ResizeObserver from 'resize-observer-polyfill';

interface DOMRectReadOnly {
  readonly bottom: number;
  readonly height: number;
  readonly left: number;
  readonly right: number;
  readonly top: number;
  readonly width: number;
  readonly x: number;
  readonly y: number;
}

interface useResizeObserverProperties {
  ref?: RefObject<Element> | null;

  element?: Element | null | undefined;

  callback?: (entry: ResizeObserverEntry) => void;
}

const IS_BROWSER = typeof window !== 'undefined';

/**
 * Watch for the resizing of a React component or Element.
 *
 * @param hookProperties - Configuration optinos for the hook.
 *
 * @returns The `DOMRect` for the observed element.
 */
export const useResizeObserver = ({
  ref,
  element,
  callback,
}: useResizeObserverProperties) => {
  const [sizes, setSizes] = useState<DOMRectReadOnly>({
    bottom: 0,
    height: 0,
    left: 0,
    right: 0,
    top: 0,
    width: 0,
    x: 0,
    y: 0,
  });

  const handleResize = (entries: ResizeObserverEntry[]) => {
    const [entry] = entries;

    if (callback) callback(entry);

    setSizes(entry.contentRect);
  };

  const [resizeObs] = useState(() =>
    IS_BROWSER ? new ResizeObserver(handleResize) : undefined,
  );

  useEffect(() => {
    if (!resizeObs) return;
    let domNode;

    if (ref) {
      domNode = ref.current;
    } else if (element) {
      domNode = element;
    }

    if (domNode) {
      resizeObs.observe(domNode);
    }

    return () => resizeObs.disconnect();
  }, [ref, resizeObs, element]);

  return sizes;
};

Step 2. In your component.tsx


import React, { useState, useRef } from 'react';
import { CarouselProvider, Slider, Slide } from 'pure-react-carousel';
import { useResizeObserver } from 'from previously created file';

const YourComponent = () => {
  const [visibleSlides, setVisibleSlides] = useState(1);
  const ref = useRef<HTMLDivElement>(null);

  // Current width of element
  const { width } = useResizeObserver({ ref });

  switch (true) {
    case width > 768 && width < 1280:
      setVisibleSlides(2);
      break;
    /**
     * Switch your cases here
     */
  }

  return (
    <div ref={ref}>
      <CarouselProvider
        naturalSlideWidth={100}
        naturalSlideHeight={125}
        totalSlides={3}
        visibleSlides={visibleSlides}
      >
        <Slider>
          <Slide index={0}>Slide 1</Slide>
          <Slide index={1}>Slide 2</Slide>
          <Slide index={2}>Slide 3</Slide>
        </Slider>
      </CarouselProvider>
    </div>
  );
};

export default YourComponent;

Btw I'll recommend you throttle setVisibleSlides calls to avoid too much re-renders while resizing the window from dev-tools.

0
On

How about setting a state and using useEffect to update the state on resize of window, then passing down the window size as a prop to the carousel component and choosing the number of visibleslides using the prop?

https://codesandbox.io/s/pure-react-carousel-responsive-visible-slides-forked-q29c1d?file=/src/App.js