Skip to main content

Product Image Carousel

A headless, flexible product image carousel component for e-commerce storefronts. Supports image navigation, thumbnail display, and variant-specific images.

Purpose

This component provides a headless carousel solution for displaying product images in e-commerce storefronts. It handles image loading, navigation, thumbnail management, and variant-specific image filtering. The component is designed to be fully customizable through its slot-based architecture and render prop pattern, allowing complete control over styling and layout while providing essential carousel functionality out of the box.

Features

  • Image carousel with navigation
  • Thumbnail navigation and display
  • Variant-specific image filtering
  • Render prop pattern for custom layouts
  • Accessibility support with ARIA attributes
  • Loading state management
  • Active slide tracking and control

Installation

npm install @haus-storefront-react/product-image-carousel

Note: This is not a public package. Contact the Haus Tech Team for access.

API Reference

ProductImageCarousel.Root

Root component for ProductImageCarousel. Provides context to all child components. Can be used with either regular children or a render prop function. Requires either an id or slug prop to identify the product.

Props

PropTypeRequiredDefaultDescription
idstringYes*-Product ID (required if slug is not provided)
slugstringYes*-Product slug (required if id is not provided)
hideThumbsIfSinglebooleanNotrueHide thumbnails if only one image
variantImagesOnlybooleanNofalseShow only variant-specific images
preloadedAssetSrcstringNo-Preloaded featured image URL shown before product assets resolve
childrenReactNode | (context) => ReactNodeNo-Either React nodes or render prop with carousel context

* Either id or slug must be provided.

ProductImageCarousel.Wrapper

Wrapper component for the entire carousel. Must be used within ProductImageCarousel.Root. Provides accessibility attributes.

Props

PropTypeRequiredDefaultDescription
asChildbooleanNofalseRender as child element instead of div

ProductImageCarousel.Carousel

Carousel component for the main carousel container. Must be used within ProductImageCarousel.Wrapper. Automatically applies data-single-image attribute when only one image.

Props

PropTypeRequiredDefaultDescription
asChildbooleanNofalseRender as child element instead of div

ProductImageCarousel.InnerWrapper

Inner wrapper component for the main image. Must be used within ProductImageCarousel.Carousel.

Props

PropTypeRequiredDefaultDescription
asChildbooleanNofalseRender as child element instead of div

ProductImageCarousel.SelectedImage

Selected image component for displaying the current image. Must be used within ProductImageCarousel.InnerWrapper. Returns null while loading.

Props

PropTypeRequiredDefaultDescription
altstringNoProduct nameAlt text for the image
asChildbooleanNofalseRender as child element instead of img

ProductImageCarousel.ThumbWrapper

Thumbnails wrapper component for the thumbnail container. Must be used within ProductImageCarousel.Carousel.

Props

PropTypeRequiredDefaultDescription
asChildbooleanNofalseRender as child element instead of div

ProductImageCarousel.Thumbnails

Thumbnails component that renders all thumbnails. Must be used within ProductImageCarousel.ThumbWrapper. Automatically hides if only one image and hideThumbsIfSingle is true.

Props

PropTypeRequiredDefaultDescription
asChildbooleanNofalseRender as child element instead of div

ProductImageCarousel.Thumb

Individual thumbnail component. Must be used within ProductImageCarousel.Thumbnails.

Props

PropTypeRequiredDefaultDescription
indexnumberYes-The thumbnail index
assetAssetYes-The asset data for the thumbnail
asChildbooleanNofalseRender as child element instead of button

ProductImageCarousel.ThumbImage

Thumbnail image component. Must be used within ProductImageCarousel.Thumb.

Props

PropTypeRequiredDefaultDescription
assetAssetYes-The asset data for the thumbnail image
asChildbooleanNofalseRender as child element instead of img

useProductImageCarousel

Custom hook that provides product image carousel functionality including asset loading, slide management, and thumbnail visibility logic. Listens for product variant selection events via the event bus.

Parameters

ParameterTypeRequiredDescription
optionsProductImageCarouselVariablesYesConfiguration object

Options Object

interface ProductImageCarouselVariables {
id?: string
slug?: string
hideThumbsIfSingle?: boolean
variantImagesOnly?: boolean
preloadedAssetSrc?: string
}

Note: Either id or slug must be provided.

Returns

Return ValueTypeDescription
productProduct | undefinedThe loaded product data or undefined while loading
isLoadingbooleanLoading state flag
assetsAsset[]Array of product assets
activeSlidenumberCurrent active slide index
setActiveSlide(index: number) => voidFunction to set the active slide
handleThumbClick(index: number) => voidFunction to handle thumbnail click
shouldShowThumbnailsbooleanWhether thumbnails should be displayed
isSingleImagebooleanWhether there is only one image

useProductImageCarouselProps

Utility hook that composes useProductImageCarousel and returns prebuilt prop getters for custom UI implementations (getWrapperProps, getThumbProps, etc.).

Parameters

ParameterTypeRequiredDescription
idstringYes*Product ID (required when slug is not provided)
slugstringYes*Product slug (required when id is not provided)
hideThumbsIfSinglebooleanNoHide thumbnail tray when only one image is available
variantImagesOnlybooleanNoRestrict assets to currently selected variant images
preloadedAssetSrcstringNoTemporary image source shown before product assets are available

* Either id or slug must be provided.

Returns

Includes all values from useProductImageCarousel plus prop getter utilities:

  • getWrapperProps
  • getCarouselProps
  • getInnerWrapperProps
  • getSelectedImageProps
  • getThumbWrapperProps
  • getThumbProps
  • getThumbImageProps
  • getThumbnailsProps

Basic Usage

Simple Component

import { ProductImageCarousel } from '@haus-storefront-react/product-image-carousel'

function ProductPage() {
return (
<ProductImageCarousel.Root id='product-id'>
<ProductImageCarousel.Wrapper>
<ProductImageCarousel.Carousel>
<ProductImageCarousel.InnerWrapper>
<ProductImageCarousel.SelectedImage />
</ProductImageCarousel.InnerWrapper>
<ProductImageCarousel.ThumbWrapper>
<ProductImageCarousel.Thumbnails />
</ProductImageCarousel.ThumbWrapper>
</ProductImageCarousel.Carousel>
</ProductImageCarousel.Wrapper>
</ProductImageCarousel.Root>
)
}

Basic Hook Usage

import { useProductImageCarousel } from '@haus-storefront-react/product-image-carousel'

function MyComponent() {
const { assets, isLoading, activeSlide } = useProductImageCarousel({
id: 'product-id',
})

if (isLoading) return <div>Loading...</div>
if (!assets.length) return <div>No images</div>

return (
<div>
<img src={assets[activeSlide]?.source} alt='Product' />
</div>
)
}

Advanced Usage

Custom Styling with asChild

import { ProductImageCarousel } from '@haus-storefront-react/product-image-carousel'

function StyledProductGallery() {
return (
<ProductImageCarousel.Root id='product-id'>
<ProductImageCarousel.Wrapper asChild>
<div className='custom-carousel-wrapper'>
<ProductImageCarousel.Carousel asChild>
<div className='custom-carousel'>
<ProductImageCarousel.InnerWrapper asChild>
<div className='custom-inner-wrapper'>
<ProductImageCarousel.SelectedImage
asChild
alt='Product image'
/>
</div>
</ProductImageCarousel.InnerWrapper>
<ProductImageCarousel.ThumbWrapper asChild>
<div className='custom-thumb-wrapper'>
<ProductImageCarousel.Thumbnails />
</div>
</ProductImageCarousel.ThumbWrapper>
</div>
</ProductImageCarousel.Carousel>
</div>
</ProductImageCarousel.Wrapper>
</ProductImageCarousel.Root>
)
}

Variant-Specific Images

import { ProductImageCarousel } from '@haus-storefront-react/product-image-carousel'

function VariantSpecificGallery() {
return (
<ProductImageCarousel.Root id='product-id' variantImagesOnly={true}>
<ProductImageCarousel.Wrapper>
<ProductImageCarousel.Carousel>
<ProductImageCarousel.InnerWrapper>
<ProductImageCarousel.SelectedImage />
</ProductImageCarousel.InnerWrapper>
<ProductImageCarousel.ThumbWrapper>
<ProductImageCarousel.Thumbnails />
</ProductImageCarousel.ThumbWrapper>
</ProductImageCarousel.Carousel>
</ProductImageCarousel.Wrapper>
</ProductImageCarousel.Root>
)
}

Custom Thumbnail Rendering

import { ProductImageCarousel } from '@haus-storefront-react/product-image-carousel'

function CustomThumbnails() {
return (
<ProductImageCarousel.Root id='product-id'>
{({ assets, activeSlide, handleThumbClick }) => (
<ProductImageCarousel.Wrapper>
<ProductImageCarousel.Carousel>
<ProductImageCarousel.InnerWrapper>
<ProductImageCarousel.SelectedImage />
</ProductImageCarousel.InnerWrapper>
<ProductImageCarousel.ThumbWrapper>
{assets.map((asset, index) => (
<ProductImageCarousel.Thumb
key={asset.id}
index={index}
asset={asset}
className={activeSlide === index ? 'active' : ''}
>
<ProductImageCarousel.ThumbImage asset={asset} />
</ProductImageCarousel.Thumb>
))}
</ProductImageCarousel.ThumbWrapper>
</ProductImageCarousel.Carousel>
</ProductImageCarousel.Wrapper>
)}
</ProductImageCarousel.Root>
)
}

Conditional Rendering Patterns

import { ProductImageCarousel } from '@haus-storefront-react/product-image-carousel'

function ConditionalProductGallery() {
return (
<ProductImageCarousel.Root id='product-id'>
{({ assets, isLoading, shouldShowThumbnails, isSingleImage }) => {
if (isLoading) {
return <div>Loading product images...</div>
}

if (!assets.length) {
return <div>No images available</div>
}

return (
<ProductImageCarousel.Wrapper>
<ProductImageCarousel.Carousel>
<ProductImageCarousel.InnerWrapper>
<ProductImageCarousel.SelectedImage />
</ProductImageCarousel.InnerWrapper>
{shouldShowThumbnails && !isSingleImage && (
<ProductImageCarousel.ThumbWrapper>
<ProductImageCarousel.Thumbnails />
</ProductImageCarousel.ThumbWrapper>
)}
</ProductImageCarousel.Carousel>
</ProductImageCarousel.Wrapper>
)
}}
</ProductImageCarousel.Root>
)
}

Made with ❤️ by Haus Tech Team