Capítulo 68 de 108

Carousel

Core Idea

Carrossel com suporte a swipe e motion, construído sobre a lib Embla Carousel. Controla tamanho/espaçamento dos slides via classes Tailwind (basis-*, pl-*/-ml-*), não via props dedicadas.

Key Concepts

  • opts: objeto repassado direto para o Embla (align, loop, direction, etc) — ver docs do Embla para todas as opções.
  • orientation: "horizontal" | "vertical".
  • plugins: array de plugins Embla (ex. embla-carousel-autoplay) passados ao Carousel.
  • setApi: prop que expõe a instância da API Embla via callback de estado, permitindo controle programático (slide atual, total, navegação, eventos .on("select", ...)).
  • Tamanho dos itens: classe basis-* (ou basis-1/2 lg:basis-1/3 responsivo) no CarouselItem, não uma prop size.
  • Espaçamento entre itens: pl-[VALUE] no CarouselItem combinado com -ml-[VALUE] no CarouselContent (offset negativo compensando o padding).

Code Examples

<Carousel className="w-full max-w-xs">
  <CarouselContent>
    {Array.from({ length: 5 }).map((_, index) => (
      <CarouselItem key={index}>
        <Card><CardContent className="flex aspect-square items-center justify-center p-6">{index + 1}</CardContent></Card>
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>
  • O que demonstra: composição mínima funcional do carrossel.
"use client"

export function CarouselDApiDemo() {
  const [api, setApi] = React.useState<CarouselApi>()
  const [current, setCurrent] = React.useState(0)
  const [count, setCount] = React.useState(0)

  React.useEffect(() => {
    if (!api) return
    setCount(api.scrollSnapList().length)
    setCurrent(api.selectedScrollSnap() + 1)
    api.on("select", () => setCurrent(api.selectedScrollSnap() + 1))
  }, [api])

  return (
    <Carousel setApi={setApi}>
      {/* ... */}
    </Carousel>
  )
}
  • O que demonstra: acesso à API imperativa do Embla via setApi para exibir "Slide X of Y" e reagir ao evento select.
const plugin = React.useRef(Autoplay({ delay: 2000, stopOnInteraction: true }))

<Carousel
  plugins={[plugin.current]}
  onMouseEnter={plugin.current.stop}
  onMouseLeave={plugin.current.reset}
>
  • O que demonstra: autoplay via plugin Embla, pausado no hover.

Reference Tables

Carousel
├── CarouselContent
│   ├── CarouselItem
│   └── CarouselItem
├── CarouselPrevious
└── CarouselNext

Anti-patterns

  • Setar basis-* sem responsividade em carrosséis multi-item: gera overflow ou itens minúsculos em telas pequenas; use breakpoints (md:basis-1/2 lg:basis-1/3).
  • RTL sem opts={{ direction: dir }}: definir só dir="rtl" no elemento não inverte a direção de scroll do Embla — é preciso passar direction explicitamente em opts.

Key Takeaways

  1. Tamanho e espaçamento de slides são 100% via Tailwind (basis-*, pl-*/-ml-*), não props do componente.
  2. setApi + useEffect é o padrão para expor estado (slide atual, contagem) ou reagir a eventos do carrossel.
  3. Plugins Embla (autoplay, etc) se conectam via prop plugins, referência estável recomendada com useRef.
  4. Em RTL, tanto dir no componente quanto direction em opts precisam ser setados, e os botões de navegação geralmente precisam de rtl:rotate-180.

Connects To

  • Card (ch064): item de carrossel comumente envolve um Card.
  • Aspect Ratio (ch069): pode ser usado dentro de CarouselItem para manter proporção consistente de mídia.