skip to Main Content

I am trying to build a carousel using swiper, I am trying to pass the SwiperSlide as children of the Swiper in this case it does not render the slides in the browser

here is how I tried:

MyCarousel.tsx:

import React from 'react';
import { Swiper } from 'swiper/react';
import 'swiper/css';

interface ComponentProps {
  children: ReactNode
}

function MyCarousel({children}: ComponentProps){
  return (
    <Swiper loop={true} spaceBetween={50} slidesPerView={1}>
      {children}
    </Swiper>
  );
};

export default MyCarousel;

MySlides.tsx:

import React from 'react'
import { SwiperSlide } from 'swiper/react'

function MySlides({ slides }: ComponentProps) {

  return (
    <>
      {slides.map((slide) => (
        <SwiperSlide className="h-full" key={slide.id}>
          <div
            className="h-full bg-cover bg-center bg-no-repeat blur-3xl"
            style={{ backgroundImage: `url(${slide.src})` }}
          ></div>
          <div className="absolute inset-0 flex h-full justify-center">
            <img src={slide.src} alt={slide.src} />
          </div>
        </SwiperSlide>
      ))}
    </>
  )
}

MyPage.tsx:

import React from 'react'
import MySlidesfrom '@components/my-slides'

function MyPage() {

  const slides = [
    { id: 1, src: 'my-image-1.jpg' },
    { id: 2, src: 'my-image-2.jpg' },
    { id: 3, src: 'my-image-3.jpg' },
  ];

  return (
    <div>
      <h1>My Page</h1>
      <CarouselProvider>
        <MySlides slides={slides} />
      </CarouselProvider>
    </div>
  );
};

The above way is not working. It is not working the slides in the DOM as you can see in the screenshot the swiper-wrapper is empty
swiper-wrapper div is empty in the DOM

If I modify MyPage.tsx this way it works as it should-

import React from 'react'

function MyPage() {

  const slides = [
    { id: 1, src: 'my-image-1.jpg' },
    { id: 2, src: 'my-image-2.jpg' },
    { id: 3, src: 'my-image-3.jpg' },
  ];

  return (
    <div>
      <h1>My Page</h1>
      <CarouselProvider>
        {slides.map((slide) => (
          <SwiperSlide className="h-full" key={slide.id}>
            <div
              className="h-full bg-cover bg-center bg-no-repeat blur-3xl"
              style={{ backgroundImage: `url(${slide.src})` }}
            ></div>
            <div className="absolute inset-0 flex h-full justify-center">
              <img src={slide.src} alt={slide.src} />
            </div>
          </SwiperSlide>
        ))}
      </CarouselProvider>
    </div>
  );
};

I am using

  • "react": "^18.3.1",
  • "swiper": "^11.1.15",

Thank you so much for your attention and participation.

2

Answers


  1. Chosen as BEST ANSWER

    I figured it out this way!

    // MyCarousel.tsx:
    import React from 'react';
    import { Swiper } from 'swiper/react';
    import 'swiper/css';
    
    interface ComponentProps {
      children: ReactNode
    }
    
    function MyCarousel({children}: ComponentProps){
    
      const enhancedChildren = React.useMemo(
        () =>
          Children.map(children, (child) =>
            isValidElement(child) ? (
              <SwiperSlide>{cloneElement(child)}</SwiperSlide>
            ) : null
          ),
        [children]
      )
    
      return (
        <Swiper loop={true} spaceBetween={50} slidesPerView={1}>
          {enhancedChildren }
        </Swiper>
      );
    };
    
    export default MyCarousel;
    
    // MySlide.tsx:
    import React from 'react'
    import { SwiperSlide } from 'swiper/react'
    
    function MySlides({ slide }: ComponentProps) {
    
      return (
        <>
          <div
            className="h-full bg-cover bg-center bg-no-repeat blur-3xl"
            style={{ backgroundImage: `url(${slide.src})` }}
          ></div>
          <div className="absolute inset-0 flex h-full justify-center">
            <img src={slide.src} alt={slide.src} />
          </div>
        </>
      )
    }
    
    // MyPage.tsx:
    import React from 'react'
    import MySlidesfrom '@components/my-slides'
    
    function MyPage() {
    
      const slides = [
        { id: 1, src: 'my-image-1.jpg' },
        { id: 2, src: 'my-image-2.jpg' },
        { id: 3, src: 'my-image-3.jpg' },
      ];
    
      return (
        <div>
          <h1>My Page</h1>
          <CarouselProvider>
            {slides.map((slide) => (
              <MySlide key={slide.id} slide={slide} />
            ))}
          </CarouselProvider>
        </div>
      );
    };
    

  2. Seems like swiper/react iterates over the children. It checks for each child if it is SwiperSlide.

    I found a trick that works, but not sure how reliable it is. If the displayName of the direct children is SwiperSlide, it renders them correctly.

    function MyCarousel({children}) {
      return (
        <Swiper
         ...
         >
          <Inner>
            {children}
          </Inner>
        </Swiper>
      );
    }
    
    // This makes the trick
    function Inner({children}) {
      return <>{children}</>
    }
    Inner.displayName = 'SwiperSlide'  // set the display name!
    
    
    // Usage:
    
     function Content() {
         return (
            <>
              <SwiperSlide>Slide 1</SwiperSlide>
              <SwiperSlide>Slide 2</SwiperSlide>
              <SwiperSlide>Slide 3</SwiperSlide>
              <SwiperSlide>Slide 4</SwiperSlide>
           </>
        )
     }
    
    function App() {
      return (
        <MyCarousel>
          <Content />
        </MyCarousel>
      )
    }
    

    Edit:

    If the above trick does not suit you, you can consider using different technique instead of children component. Like a render prop or utility function

    utility function instead of component:

    function MyCarousel({children}) {
      return (
        <Swiper
         ...
         >
            {children}
        </Swiper>
      );
    }
    
    function makeSlides(slides) {
        return slides.map((slide) => ...view here)
    }
    
    
    function App() {
      return (
        <MyCarousel>
          {makeSlides(slides)}
        </MyCarousel>
      )
    }
    

    render prop that renders single slide:

    function MyCarousel({slides, renderSlide}) {
      return (
        <Swiper
         ...
         >
            {
                slides.map(renderSlide)
            }
        </Swiper>
      );
    }
    
    function renderSlide(slide) {
        return ...single slide view here
    }
    
    function App() {
      return (
        <MyCarousel slides={slides} renderSlide={renderSlide} />
      )
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search