Skip to content
gnosticaignostic-ui
Esc
↑↓navigate↵open⌘Jpreview
On this page

Lightbox Carousel

Astro image carousels that open the chosen slide in a larger view.

---
import { Image } from "astro:assets"
import {
  LightboxCarousel,
  LightboxCarouselContent,
  LightboxCarouselItem,
  LightboxCarouselNext,
  LightboxCarouselPrevious,
} from "@/components/ui/lightbox-carousel"

const images = [
  {
    src: "https://images.unsplash.com/photo-1470770841072-f978cf4d019e?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "Mountains reflected in a lake",
  },
  {
    src: "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "Sunlight through forest trees",
  },
  {
    src: "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "Open landscape in evening light",
  },
  {
    src: "https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "A mountain peak above the clouds",
  },
  {
    src: "https://images.unsplash.com/photo-1472396961693-142e6e269027?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "Deer in a green forest",
  },
  {
    src: "https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "A path through tall trees",
  },
]
---

<div class="mx-auto w-full max-w-3xl px-14 py-4">
  <LightboxCarousel>
    <LightboxCarouselContent>
      {images.map((image) => (
        <LightboxCarouselItem alt={image.alt}>
          <Image
            slot="preview"
            {...image}
            alt=""
            class="aspect-3/2 w-full object-cover"
            loading="lazy"
          />
          <Image slot="content" {...image} loading="lazy" />
        </LightboxCarouselItem>
      ))}
    </LightboxCarouselContent>
    <LightboxCarouselPrevious />
    <LightboxCarouselNext />
  </LightboxCarousel>
</div>
Open preview in a new tab

Installation

bunx --bun shadcn@latest add @gnosticai/lightbox-carousel

The command also installs Lightbox and Carousel.

Usage

Compose the page carousel from the same parts as Carousel. Each item needs a preview slot and a content slot. preview is the slide on the page. content is the slide in the enlarged dialog. Click a slide to open that image. Previous and Next in the dialog continue through the same list. Close the dialog and the page carousel shows the slide you were viewing.

---
import { Image } from "astro:assets"
import {
  LightboxCarousel,
  LightboxCarouselContent,
  LightboxCarouselItem,
  LightboxCarouselNext,
  LightboxCarouselPrevious,
} from "@/components/ui/lightbox-carousel"
import lake from "../assets/lake.jpg"
---

<LightboxCarousel>
  <LightboxCarouselContent>
    <LightboxCarouselItem alt="Mountains reflected in a lake">
      <Image slot="preview" src={lake} alt="" class="aspect-3/2 w-full object-cover" />
      <Image slot="content" src={lake} alt="Mountains reflected in a lake" />
    </LightboxCarouselItem>
  </LightboxCarouselContent>
  <LightboxCarouselPrevious />
  <LightboxCarouselNext />
</LightboxCarousel>

Arrow keys move the open carousel. Escape, the close button, and an overlay click close the dialog. The carousel loops.

Keep links and buttons out of preview. That slot is already inside a button.

Several slides

Set the item basis to show more than one slide. caption appears under the enlarged image.

---
import { Image } from "astro:assets"
import {
  LightboxCarousel,
  LightboxCarouselContent,
  LightboxCarouselItem,
  LightboxCarouselNext,
  LightboxCarouselPrevious,
} from "@/components/ui/lightbox-carousel"

const images = [
  {
    src: "https://images.unsplash.com/photo-1470770841072-f978cf4d019e?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "Mountains reflected in a lake",
    caption: "A quiet mountain lake.",
  },
  {
    src: "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "Sunlight through forest trees",
    caption: "Morning light between the trees.",
  },
  {
    src: "https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "A mountain peak above the clouds",
    caption: "The ridge stays above the cloud line.",
  },
  {
    src: "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?w=1200&h=800&auto=format&fit=crop",
    width: 1200,
    height: 800,
    alt: "Open landscape in evening light",
    caption: "The valley holds the last light.",
  },
]
---

<div class="mx-auto w-full max-w-3xl px-14 py-4">
  <LightboxCarousel label="Landscape gallery">
    <LightboxCarouselContent class="-ml-4">
      {images.map(({ caption, ...image }, i) => (
        <LightboxCarouselItem
          alt={image.alt}
          caption={caption}
          class="basis-1/2 pl-4 @min-xl/carousel:basis-1/3"
        >
          <Image
            slot="preview"
            {...image}
            alt={`Image ${i + 1}`}
            class="aspect-3/2 w-full object-cover"
            loading={i === 0 ? "eager" : "lazy"}
          />
          <Image slot="content" {...image} loading="lazy" />
        </LightboxCarouselItem>
      ))}
    </LightboxCarouselContent>
    <LightboxCarouselPrevious />
    <LightboxCarouselNext />
  </LightboxCarousel>
</div>
Open preview in a new tab

API

LightboxCarousel accepts native div attributes and these options:

Prop Type Default Purpose
label string Image gallery Accessible name for the page carousel.

LightboxCarouselContent accepts the same attributes as CarouselContent. LightboxCarouselPrevious and LightboxCarouselNext accept the same attributes as the carousel controls.

LightboxCarouselItem accepts native div attributes and these options:

Prop Type Default Purpose
alt string Required Slide button name and dialog title.
caption string — Optional text under the enlarged image.
hoverZoom boolean true Scale the slide preview on hover with hover:scale-105.

Both preview and content slots are required. The page carousel is one slide wide unless you set basis on the item. Spacing follows Carousel: pair a negative margin on LightboxCarouselContent with padding on each item.

Compose the thumbnail carousel and lightbox parts around the same ordered images. Use stable image order so the enlarged view matches the selected thumbnail. Pass carousel options through the existing props.

Keyboard and behavior

Embla supplies slide movement and the dialog controller supplies modal focus. Keep labelled previous, next, and close controls. Check keyboard use in both the page carousel and the enlarged view.

Theme and layout

Use small thumbnail sources and suitable full-size images to limit transfer size. Check aspect ratios and captions at narrow widths. The component does not generate image derivatives or load a media library for you.

Use Carousel without a modal or Lightbox Grid for a page of thumbnails.

Was this page helpful?