ProductImage
Product detail media gallery with synced active image state, variant-aware image selection, thumbnail rails, dots, and lightbox-first viewing.
Best for
Import
import { ProductImage } from "@enadhq/enad-react-sdk/client/storefront"When to use it
Use ProductImage as the main media gallery on a product detail page. It handles the high-importance browsing behavior around product imagery, including thumbnails, lightbox viewing, a consistent hero image frame, synced active-image state, and variant-aware image handoff.
If the page only needs a single static image, a simpler image block is usually enough.
Composition notes
The gallery is prop-driven:
imagesis the required media array.- each image can include
variantmetadata so the gallery knows which simple selector value it belongs to. - each image can include
variantIdmetadata so the gallery can also match a resolved backend variant id. activeVariantIdtakes priority and activates the first image whosevariantIdmatches the selected value.activeVariantactivates the first image whosevariantmatches when noactiveVariantIdis provided.aspectRatiocontrols whether the frame reads square or portrait.interactioncontrols whether the gallery opens the fullscreen lightbox on click or tap. The default is"lightbox".activeIndex,defaultActiveIndex, andonActiveIndexChangecontrol the inline carousel, thumbnail rail, and the lightbox handoff.lightboxOpen,defaultLightboxOpen, andonLightboxOpenChangelet a parent own the fullscreen viewer state when needed.showThumbnails,thumbnailPosition, andmaxThumbnailsshape the secondary navigation rail.arrowschooses whether navigation sits inside or outside the frame.showDotsis useful when the layout is tighter and thumbnails would be too heavy.viewTransitionsis a progressive-enhancement hook for shared-element continuity between the PDP hero and the fullscreen viewer.
Start with the content and device needs, then choose the navigation affordances that match.
Behavior and theming guidance
Use 4:5 when the product is fashion- or portrait-led. Use 1:1 when the assortment benefits from a more catalog-like square rhythm.
Thumbnail rails add clarity but also visual weight. On compact surfaces, dots plus arrows often give a cleaner result. When the image set is longer than the visible thumbnail count, the rail fades at the edge to hint at overflow without feeling heavy.
classNames now exposes stable hooks for the thumbnail rail, thumbnail buttons, active-thumbnail state, thumbnail images, and overflow indicator. That lets one PDP keep a soft, editorial thumbnail treatment while another uses harder technical framing without reaching into aria labels or private DOM structure.
For variant-heavy PDPs, decide whether your page state is still axis-level or already resolved:
- use
activeVariantwhen the selector state is still a simple value like"oak"or"black" - use
activeVariantIdwhen your product logic has already resolved a concrete purchasable variant like"sku-oak-001"
If both are present, ProductImage prioritizes activeVariantId. If several images belong to the same variant or variant id, the gallery activates the first matching image and the visitor can continue browsing from there.
Always keep alt text meaningful because the gallery is a primary part of the PDP experience. If you opt into shared-element continuity, keep image identities stable across variant switches so the transition still feels like the same object going deeper.
Examples
Live examples you can edit directly in the sandbox.
PDP gallery with thumbnails
A strong default for product detail pages that need fullscreen viewing and clear image browsing without letting the thumbnail rail take over the layout.
Editorial thumbnails vs technical thumbnails
Use semantic classNames hooks on the thumbnail rail to make the same gallery feel either soft and editorial or sharp and technical.
Controlled gallery and lightbox state
Use controlled state when the inline gallery, external controls, and fullscreen viewer all need to stay in sync.
Variant-linked gallery
Use external selector state to activate the first image that belongs to the selected material or color value.
Resolved variant-id gallery
Use a resolved backend variant id when the product model has already selected a specific purchasable variant.
Slots
Component Sets
Preview the first example across the available component-set presets to compare tone, spacing, and structural defaults.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| images | ProductImageAsset[] | — | No description yet. |
| activeVariant | string | — | Active variant key used to select the first matching image in the gallery |
| activeVariantId | string | — | Active resolved variant id used to select the first matching image in the gallery |
| aspectRatio | string | 4:5 | No description yet. |
| interaction | string | lightbox | Enhanced interaction model for the hero media. "lightbox" opens the fullscreen viewer on click or tap. |
| activeIndex | number | — | Controlled active image index for the carousel, thumbnail rail, and lightbox. |
| defaultActiveIndex | number | 0 | Uncontrolled initial active image index. |
| onActiveIndexChange | string | — | Callback fired when the active image changes. |
| lightboxOpen | boolean | — | Controlled lightbox open state. |
| defaultLightboxOpen | boolean | false | Uncontrolled initial lightbox open state. |
| onLightboxOpenChange | string | — | Callback fired when the lightbox open state changes. |
| arrows | string | undefined | 'inset' — arrows float over the image edges; 'outset' — arrows sit outside the image bounds |
| showDots | boolean | true | No description yet. |
| showThumbnails | boolean | false | No description yet. |
| maxThumbnails | number | 5 | Maximum number of thumbnails visible before overflow hinting appears. Default: 5 |
| thumbnailPosition | string | left | Where to place the thumbnail strip when showThumbnails is true. Default: "left" |
| imageLoading | string | — | Loading behavior for main gallery images. By default the active image uses browser loading behavior and inactive rendered slides are lazy. |
| viewTransitions | string | — | Progressive enhancement hook for environments that support shared-element continuity. |
| classNames | Partial<Record<import("@enadhq/enad-react-sdk/client/storefront/types").ProductImageSlot, string>> | — | No description yet. Slot keys rootcarouselthumbnailRailthumbnailButtonthumbnailButtonActivethumbnailImageoverflowIndicator |
| className | string | — | No description yet. |