Blossom Carousel Logo
GitHub
Framework Guides

React and Next.js

Use Blossom Carousel in React and Next.js apps with tsx, global styles, and semantic root elements.

Install

pnpm add @blossom-carousel/react

React usage

import { BlossomCarousel } from "@blossom-carousel/react";
import "@blossom-carousel/react/style.css";

export function App() {
  return (
    <BlossomCarousel>
      {Array.from({ length: 12 }, (_, index) => (
        <div key={index}>Slide {index + 1}</div>
      ))}
    </BlossomCarousel>
  );
}

Next.js setup

Import the stylesheet from a global entry point.

app/layout.tsx
import "@blossom-carousel/react/style.css";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

Then render the carousel from a component.

BlossomCarousel.tsx
import { BlossomCarousel } from "@blossom-carousel/react";

export function FeaturedCarousel() {
  return (
    <BlossomCarousel>
      {Array.from({ length: 12 }, (_, index) => (
        <div key={index}>Slide {index + 1}</div>
      ))}
    </BlossomCarousel>
  );
}

Semantic root element

Use as="ul" for list-like carousels.

<BlossomCarousel as="ul">
  {Array.from({ length: 12 }, (_, index) => (
    <li key={index}>Slide {index + 1}</li>
  ))}
</BlossomCarousel>;

Place previous, next, and dot controls outside the carousel with <BlossomPrev>, <BlossomNext>, and <BlossomDots>. Link them to the carousel with an id on <BlossomCarousel>, and a matching for prop on each control and mark slides with data-blossom-slide.

<BlossomCarousel id="my-carousel">
  {Array.from({ length: 12 }, (_, index) => (
    <div key={index} data-blossom-slide>
      Slide {index + 1}
    </div>
  ))}
</BlossomCarousel>

<BlossomPrev for="my-carousel" />
<BlossomDots for="my-carousel" />
<BlossomNext for="my-carousel" />

Prev/Next Buttons

<BlossomPrev> and <BlossomNext> are aware of configured scroll-snap and will navigate between snap points. When no scroll-snap is configured, they will slide the carousel proportionally.

Slot your own content to replace the default button icon.

<BlossomPrev for="my-carousel">
  <span>Previous</span>
</BlossomPrev>;

Dots

<BlossomDots> renders one button per slide marked with data-blossom-slide. Default styles can be themed with CSS custom properties on the component or any ancestor:

/* defaults */
{
  --blossom-dot-size: 0.625rem;
  --blossom-dot-radius: 50%;
  --blossom-dot-color: currentColor;
  --blossom-dot-opacity: 0.35;
  --blossom-dot-hover-opacity: 0.6;
  --blossom-dot-active-opacity: 1;
}

To bring your own dots, provide a default slot and render <BlossomDot> inside it. This will configure the dot as a <button> with navigation wired up. Now you can style the dot as you please and attach any button attributes you need.

<BlossomDots for="my-carousel">
  {({ index, active }) => (
    <BlossomDot
      className="my-dot"
      data-active={active}
      aria-label={`Photo ${index + 1}`}
    >
      {index + 1}
    </BlossomDot>
  )}
</BlossomDots>;

Listening for commands

Listen for command events on the carousel to know when any navigation control is triggered:

  • previous (--blossom-prev)
  • next (--blossom-next)
  • dot (--blossom-goto-{index}).

These events are not fired by drag or free scrolling. Read event.command (or event.detail.command where the Invoker Commands polyfill applies).

<BlossomCarousel onCommand={handleCommand}>
  {Array.from({ length: 12 }, (_, index) => (
    <div key={index} data-blossom-slide>
      Slide {index + 1}
    </div>
  ))}
</BlossomCarousel>;

function handleCommand(event: CustomEvent) {
  const command = event?.command || event?.detail?.command;
}

See the Buttons and Dots examples for live demos.

Overscroll API

Tap into Blossom's drag engine's overscroll behavior to create your own style.

import { useRef } from "react";
import { BlossomCarousel } from "@blossom-carousel/react";

export function App() {
  const blossomCarousel = useRef<HTMLElement | null>(null);

  function onOverscroll(event: CustomEvent<{ left: number }>) {
    // prevent and overwrite Blossom's default rubberbanding effect
    event.preventDefault();

    const overScroll = event.detail.left;

    Array.from(blossomCarousel.current?.children ?? []).forEach((slide) => {
      (slide as HTMLElement).style.transform = `scale(${1 - overScroll * 0.1})`;
    });
  }

  return (
    <BlossomCarousel
      ref={blossomCarousel}
      onOverscroll={(event) => {
        onOverscroll(event as CustomEvent<{ left: number }>);
      }}
    >
      {Array.from({ length: 12 }, (_, index) => (
        <div key={index}>Slide {index + 1}</div>
      ))}
    </BlossomCarousel>
  );
}