Creative Tim UICreative Tim UI

Shadcn Carousel

PreviousNext

A modern, responsive carousel component built with Swiper for displaying images and content in a slideshow format.

Slide 1
Slide 2
Slide 3
"use client"

import Image from "next/image"
import { Navigation, Pagination } from "swiper/modules"
import { Swiper, SwiperSlide } from "swiper/react"

import "swiper/css"
import "swiper/css/navigation"
import "swiper/css/pagination"

export function CarouselDemo() {
  const images = [
    "https://raw.githubusercontent.com/creativetimofficial/public-assets/refs/heads/master/creative-ai/bg-3.jpg",
    "https://raw.githubusercontent.com/creativetimofficial/public-assets/refs/heads/master/creative-ai/bg-2.jpg",
    "https://images.unsplash.com/photo-1501854140801-50d01698950b?auto=format&w=2000&q=85",
  ]

  return (
    <>
      <style jsx global>{`
        .swiper-button-next,
        .swiper-button-prev {
          color: white !important;
          width: 24px !important;
          height: 24px !important;
        }
        .swiper-button-next:after,
        .swiper-button-prev:after {
          font-size: 24px !important;
        }
        .swiper-pagination-bullet {
          background: white !important;
        }
      `}</style>
      <div className="w-full max-w-4xl">
        <Swiper
          modules={[Navigation, Pagination]}
          navigation
          pagination={{ clickable: true }}
          spaceBetween={30}
          slidesPerView={1}
          className="rounded-lg"
        >
          {images.map((image, index) => (
            <SwiperSlide key={index}>
              <div className="relative aspect-video w-full overflow-hidden rounded-lg">
                <img
                  src={image}
                  alt={`Slide ${index + 1}`}
                  className="object-cover"
                />
              </div>
            </SwiperSlide>
          ))}
        </Swiper>
      </div>
    </>
  )
}

Installation

pnpm dlx @creative-tim/ui@latest add carousel

Required Dependency

Usage

import Image from "next/image"
import { Navigation, Pagination } from "swiper/modules"
import { Swiper, SwiperSlide } from "swiper/react"
 
import "swiper/css"
import "swiper/css/navigation"
import "swiper/css/pagination"
 
export default function MyCarousel() {
  return (
    <Swiper
      modules={[Navigation, Pagination]}
      navigation
      pagination={{ clickable: true }}
      spaceBetween={30}
      slidesPerView={1}
    >
      <SwiperSlide>
        <Image src="/image1.jpg" alt="Slide 1" width={800} height={400} />
      </SwiperSlide>
      <SwiperSlide>
        <Image src="/image2.jpg" alt="Slide 2" width={800} height={400} />
      </SwiperSlide>
    </Swiper>
  )
}

Examples

A simple carousel with navigation arrows and pagination dots.

Slide 1
Slide 2
Slide 3
"use client"

import Image from "next/image"
import { Navigation, Pagination } from "swiper/modules"
import { Swiper, SwiperSlide } from "swiper/react"

import "swiper/css"
import "swiper/css/navigation"
import "swiper/css/pagination"

export function CarouselDemo() {
  const images = [
    "https://raw.githubusercontent.com/creativetimofficial/public-assets/refs/heads/master/creative-ai/bg-3.jpg",
    "https://raw.githubusercontent.com/creativetimofficial/public-assets/refs/heads/master/creative-ai/bg-2.jpg",
    "https://images.unsplash.com/photo-1501854140801-50d01698950b?auto=format&w=2000&q=85",
  ]

  return (
    <>
      <style jsx global>{`
        .swiper-button-next,
        .swiper-button-prev {
          color: white !important;
          width: 24px !important;
          height: 24px !important;
        }
        .swiper-button-next:after,
        .swiper-button-prev:after {
          font-size: 24px !important;
        }
        .swiper-pagination-bullet {
          background: white !important;
        }
      `}</style>
      <div className="w-full max-w-4xl">
        <Swiper
          modules={[Navigation, Pagination]}
          navigation
          pagination={{ clickable: true }}
          spaceBetween={30}
          slidesPerView={1}
          className="rounded-lg"
        >
          {images.map((image, index) => (
            <SwiperSlide key={index}>
              <div className="relative aspect-video w-full overflow-hidden rounded-lg">
                <img
                  src={image}
                  alt={`Slide ${index + 1}`}
                  className="object-cover"
                />
              </div>
            </SwiperSlide>
          ))}
        </Swiper>
      </div>
    </>
  )
}

A carousel featuring thumbnail navigation for quick slide access.

Slide 1
Slide 2
Slide 3
Thumbnail 1
Thumbnail 2
Thumbnail 3
"use client"

import { useState } from "react"
import Image from "next/image"
import type { Swiper as SwiperType } from "swiper"
import { FreeMode, Navigation, Thumbs } from "swiper/modules"
import { Swiper, SwiperSlide } from "swiper/react"

import "swiper/css"
import "swiper/css/free-mode"
import "swiper/css/navigation"
import "swiper/css/thumbs"

export function CarouselWithThumbnails() {
  const [thumbsSwiper, setThumbsSwiper] = useState<SwiperType | null>(null)

  const images = [
    "https://raw.githubusercontent.com/creativetimofficial/public-assets/refs/heads/master/creative-ai/bg-3.jpg",
    "https://raw.githubusercontent.com/creativetimofficial/public-assets/refs/heads/master/creative-ai/bg-2.jpg",
    "https://images.unsplash.com/photo-1501854140801-50d01698950b?auto=format&w=2000&q=85",
  ]

  return (
    <>
      <style jsx global>{`
        .swiper-button-next,
        .swiper-button-prev {
          color: white !important;
          width: 24px !important;
          height: 24px !important;
        }
        .swiper-button-next:after,
        .swiper-button-prev:after {
          font-size: 24px !important;
        }
      `}</style>
      <div className="w-full max-w-4xl space-y-4">
        <Swiper
          modules={[FreeMode, Navigation, Thumbs]}
          spaceBetween={10}
          navigation
          thumbs={{ swiper: thumbsSwiper }}
          className="rounded-lg"
        >
          {images.map((image, index) => (
            <SwiperSlide key={index}>
              <div className="relative aspect-video w-full overflow-hidden rounded-lg">
                <img
                  src={image}
                  alt={`Slide ${index + 1}`}
                  className="object-cover"
                />
              </div>
            </SwiperSlide>
          ))}
        </Swiper>

        <Swiper
          onSwiper={setThumbsSwiper}
          modules={[FreeMode, Navigation, Thumbs]}
          spaceBetween={10}
          slidesPerView={3}
          freeMode
          watchSlidesProgress
          className="rounded-lg"
        >
          {images.map((image, index) => (
            <SwiperSlide key={index} className="cursor-pointer">
              <div className="relative aspect-video w-full overflow-hidden rounded-lg">
                <img
                  src={image}
                  alt={`Thumbnail ${index + 1}`}
                  className="object-cover"
                />
              </div>
            </SwiperSlide>
          ))}
        </Swiper>
      </div>
    </>
  )
}

An auto-advancing carousel with dynamic pagination bullets.

Slide 1
Slide 2
Slide 3
"use client"

import Image from "next/image"
import { Autoplay, Pagination } from "swiper/modules"
import { Swiper, SwiperSlide } from "swiper/react"

import "swiper/css"
import "swiper/css/pagination"

export function CarouselAutoplay() {
  const images = [
    "https://raw.githubusercontent.com/creativetimofficial/public-assets/refs/heads/master/creative-ai/bg-3.jpg",
    "https://raw.githubusercontent.com/creativetimofficial/public-assets/refs/heads/master/creative-ai/bg-2.jpg",
    "https://images.unsplash.com/photo-1501854140801-50d01698950b?auto=format&w=2000&q=85",
  ]

  return (
    <>
      <style jsx global>{`
        .swiper-pagination-bullet {
          background: white !important;
        }
      `}</style>
      <div className="w-full max-w-4xl">
        <Swiper
          modules={[Autoplay, Pagination]}
          autoplay={{
            delay: 3000,
            disableOnInteraction: false,
          }}
          pagination={{
            clickable: true,
            dynamicBullets: true,
          }}
          loop
          spaceBetween={30}
          slidesPerView={1}
          className="rounded-lg"
        >
          {images.map((image, index) => (
            <SwiperSlide key={index}>
              <div className="relative aspect-video w-full overflow-hidden rounded-lg">
                <img
                  src={image}
                  alt={`Slide ${index + 1}`}
                  className="object-cover"
                />
              </div>
            </SwiperSlide>
          ))}
        </Swiper>
      </div>
    </>
  )
}

Features

  • Navigation: Previous/Next arrow buttons for manual slide control
  • Pagination: Clickable dots for direct slide access
  • Thumbnails: Thumbnail navigation for enhanced user experience
  • Autoplay: Automatic slide progression with customizable delay
  • Responsive: Fully responsive design that works on all screen sizes
  • Loop: Infinite loop mode for continuous sliding
  • Customizable: Extensive configuration options through Swiper API

Swiper Modules

The carousel component uses various Swiper modules for different features:

  • Navigation: Adds previous/next buttons
  • Pagination: Adds pagination dots
  • Autoplay: Enables automatic slide progression
  • Thumbs: Enables thumbnail navigation
  • FreeMode: Allows free scrolling without snapping

Configuration

You can customize the carousel behavior using Swiper options:

<Swiper
  spaceBetween={30} // Space between slides
  slidesPerView={1} // Number of slides per view
  loop={true} // Enable infinite loop
  autoplay={{
    delay: 3000, // Delay between slides in ms
    disableOnInteraction: false,
  }}
  pagination={{
    clickable: true, // Enable clickable pagination
    dynamicBullets: true, // Dynamic bullet sizing
  }}
>
  {/* Slides */}
</Swiper>

Best Practices

  1. Image Optimization: Use Next.js Image component for optimal performance
  2. Alt Text: Always provide descriptive alt text for accessibility
  3. Aspect Ratio: Maintain consistent aspect ratios across all slides
  4. Loading Priority: Set priority={true} for the first slide to improve LCP
  5. Autoplay Delay: Use 3000-5000ms delay for comfortable viewing experience

Resources