Documentation

React Photo Album allows you to build a responsive React photo gallery in minutes. To get started, follow the Installation and Minimal Setup Example guides, or feel free to explore the collection of examples with live demos.

Parameters marked with an asterisk () are required.

Prerequisites

React Photo Album provides four separate photo album components, depending on the layout you intend to use. Each component comes with its own CSS stylesheet.

Rows Layout

import { RowsPhotoAlbum } from "react-photo-album";
import "react-photo-album/rows.css";

Columns Layout

import { ColumnsPhotoAlbum } from "react-photo-album";
import "react-photo-album/columns.css";

Masonry Layout

import { MasonryPhotoAlbum } from "react-photo-album";
import "react-photo-album/masonry.css";

3-in-1

If you use more than one layout in our app, you may opt for the aggregate component, which bundles all three layouts.

import PhotoAlbum from "react-photo-album";
import "react-photo-album/styles.css";

Common Props

The following props are applicable to all three layouts.

PropertyTypeDescription
photosPhoto[]

An array of photos to display in the photo album. See Photo for details.

spacingnumber | function

Spacing between images (similar to CSS grid gap).

Default responsive value:

  • 20, when container width >= 1200
  • 15, when container width >= 600 and < 1200
  • 10, when container width >= 300 and < 600
  • 5, when container width < 300
paddingnumber | function

Padding around each image.

Default value: 0

onClickfunction
Photo click callback. See Click Handler for details.
sizesobject

Photo album container width in various viewports. See Sizes for details.

breakpointsnumber[]

Photo album layout breakpoints. See Breakpoints for details.

componentsPropsobject | function

Additional HTML attributes to be passed to the rendered elements. See Components Props for details.

renderobject | function

Custom render functions. See Render Functions for details.

defaultContainerWidthnumber

The default container width in server-side rendering (SSR). See Default Container Width for details.

skeletonReactNode

Fallback skeleton in SSR. See Skeleton for details.

RowsPhotoAlbum Props

PropertyTypeDescription
targetRowHeightnumber | function

Target row height.

Default responsive value:

  • (container width) / 5, when container width >= 1200
  • (container width) / 4, when container width >= 600 and < 1200
  • (container width) / 5, when container width >= 300 and < 600
  • (container width) / 2, when container width < 300
rowConstraintsobject | function

Additional row constraints.

  • minPhotos - minimum number of photos per row
  • maxPhotos - maximum number of photos per row
  • singleRowMaxHeight - maximum row height when there is not enough photos to fill more than one row

Usage example:

<RowsPhotoAlbum
  photos={photos}
  targetRowHeight={150}
  rowConstraints={{ singleRowMaxHeight: 250 }}
/>

ColumnsPhotoAlbum Props

PropertyTypeDescription
columnsnumber | function

Number of columns.

Default responsive value:

  • 5, when container width >= 1200
  • 4, when container width >= 600 and < 1200
  • 3, when container width >= 300 and < 600
  • 2, when container width < 300

Usage example:

<ColumnsPhotoAlbum photos={photos} columns={4} />

MasonryPhotoAlbum Props

MasonryPhotoAlbum accepts columns prop identical to the one supported by the ColumnsPhotoAlbum.

Usage example:

<MasonryPhotoAlbum photos={photos} columns={4} />

PhotoAlbum Props

The aggregate PhotoAlbum component supports all relevant props that correspond to the selected layout.

PropertyTypeDescription
layout"columns" | "rows" | "masonry"Photo album layout type.

Usage example:

<PhotoAlbum layout="rows" photos={photos} targetRowHeight={150} />

Photo

PropertyTypeDescription
srcstringImage source.
widthnumberImage width in pixels.
heightnumberImage height in pixels.
keystring
React key attribute.
altstring
Image alt attribute.
titlestring
Image title attribute.
hrefstringImage link URL.
labelstringARIA label for the link and button elements.
srcSet

{
  src: string;
  width: number;
  height: number;
}[]

Optional array of alternative images to be included in the srcset attribute.

All images in a given Photo object must be of the same aspect ratio.

You can also provide custom photo attributes and access them in the render functions.

Click Handler

You can add interactive behavior by providing the onClick callback.

<RowsPhotoAlbum
  photos={photos}
  onClick={({ index }) => {
    openLightbox(index);
  }}
/>

The callback function accepts a single parameter with the following props:

PropertyTypeDescription
indexnumber
Photo index in the original photos array.
photoPhotoPhoto object.
eventMouseEventCorresponding mouse event.

Responsive Props

React Photo Album accepts various props as responsive parameters.

Responsive props can be provided either as a hard-coded value:

<ColumnsPhotoAlbum photos={photos} columns={3} />

or as a function of container width:

<ColumnsPhotoAlbum
  photos={photos}
  columns={(containerWidth) => {
    if (containerWidth < 400) return 2;
    if (containerWidth < 800) return 3;
    return 4;
  }}
/>

Components Props

You can pass additional HTML attributes to the rendered elements through the componentsProps parameter.

PropertyTypeDescription
containerComponentProps<"div">
Additional HTML attributes for the outer div container.
trackComponentProps<"div">
Additional HTML attributes for the row / column div containers.
wrapperComponentProps<"div">
Additional HTML attributes for the image div wrapper.
linkComponentProps<"a">
Additional HTML attributes for the photo a link.
buttonComponentProps<"button">
Additional HTML attributes for the photo button element.
imageComponentProps<"img">

Additional HTML attributes for the photo img element.

Default: { loading: "lazy", decoding: "async"}

Usage example:

<RowsPhotoAlbum
  photos={photos}
  componentsProps={(containerWidth) => ({
    image: { loading: (containerWidth || 0) > 600 ? "eager" : "lazy" },
  })}
/>

Render Functions

React Photo Album allows you to customize all rendered elements by supplying your own custom render functions. Each render function provides the default element's props as a first parameter. These typically include style and className attributes the default implementation requires.

PropertyTypeDescription
container(props) => ReactNode

Render custom container. See Container for details.

track(props) => ReactNode

Render custom row / column container. See Track for details.

wrapper(props, context) => ReactNode

Render custom image wrapper. See Wrapper for details.

link(props, context) => ReactNode

Render custom link element. See Link for details.

button(props, context) => ReactNode

Render custom button element. See Button for details.

image(props, context) => ReactNode

Render custom image element. See Image for details.

extras(props, context) => ReactNode

Render custom markup immediately after each image. See Extras for details.

photo(props, context) => ReactNode

Render custom photo. See Photo for details.

When applicable, the second parameter represents the photo rendering context.

PropertyTypeDescription
photoPhotoPhoto object.
indexnumber
Photo index in the original photos array.
widthnumberRendered photo width in pixels.
heightnumberRendered photo height in pixels.

Container

You can customize the photo album div container through the render.container prop. Your implementation must forward ref attribute to the underlying container element.

<RowsPhotoAlbum
  photos={photos}
  render={{
    container: ({ ref, ...rest }) => <div ref={ref} {...rest} />,
  }}
/>

Track

You can customize the row / column div containers through the render.track prop. This is not common.

<RowsPhotoAlbum
  photos={photos}
  render={{
    track: (props) => <div {...props} />,
  }}
/>

Wrapper

You can customize the image wrapper through the render.wrapper prop. This wrapper is rendered only when photos are not clickable (there is no href photo prop and no onClick callback). This is not common.

<RowsPhotoAlbum
  photos={photos}
  render={{
    wrapper: (props) => <div {...props} />,
  }}
/>

Link element is rendered when a photo has an href attribute. You can provide your own link implementation through the render.link prop.

<RowsPhotoAlbum
  photos={photos}
  render={{
    link: (props) => <a {...props} />,
  }}
/>

Button

Button element is rendered when the photo album has an onClick callback. You can provide your own button implementation through the render.button prop.

<RowsPhotoAlbum
  photos={photos}
  render={{
    button: (props) => <button {...props} />,
  }}
/>

Image

You can provide your own image implementation through the render.image prop.

<RowsPhotoAlbum
  photos={photos}
  render={{
    image: (props) => <img {...props} />,
  }}
/>

Extras

You can render custom elements alongside each photo. This can be useful for rendering interactive icons with position: absolute.

<RowsPhotoAlbum
  photos={photos}
  render={{
    extras: (_, { photo, index }) => (
      <FavoriteIcon photo={photo} index={index} />
    ),
  }}
/>

Photo

This is the render function that completely overrides the default wrapper, link, button, image and extras render functions. The only prop provided in the first argument, is the onClick callback.

<RowsPhotoAlbum
  photos={photos}
  render={{
    photo: ({ onClick }, { photo, width, height }) => (
      <CustomPhoto
        photo={photo}
        width={width}
        height={height}
        onClick={onClick}
      />
    ),
  }}
/>

Breakpoints

By default, React Photo Album re-calculates its layout every time its container width changes. For example, the layout may be re-calculated dozens of times during a browser window resize. If this behavior is undesired, you can avoid it by providing the breakpoints prop (e.g., [300, 600, 1200]). When the breakpoints parameter is defined, React Photo Album calculates the layout only once per the breakpoint interval.

<RowsPhotoAlbum photos={photos} breakpoints={[300, 600, 1200]} />

Sizes

Photo album components automatically generate sizes and srcset image attributes when photo objects contain srcSet array. By default, React Photo Album assumes that the photo album utilizes approximately 100vw of the page width. If that's not the case, you can improve the performance of your responsive images by describing your photo album size in different viewports.

For example, this website uses the following sizes attribute to account for the content container padding and the left-hand side navigation menu:

<RowsPhotoAlbum
  photos={photos}
  sizes={{
    size: "992px",
    sizes: [
      { viewport: "(max-width: 767px)", size: "calc(100vw - 32px)" },
      { viewport: "(max-width: 1279px)", size: "calc(100vw - 288px)" },
    ],
  }}
/>

Styling Focus Indicators

React Photo Album does not provide an opinionated styling for keyboard focus indicators, so buttons and links are styled with browser-default focus indicator styles. You can implement your own styles to match your website design. Here is an example you can use as a starting point.

.react-photo-album--link:focus-visible,
.react-photo-album--button:focus-visible {
  outline: 9px double white;
  box-shadow: 0 0 0 6px black;
}

@supports not selector(:focus-visible) {
  .react-photo-album--link:focus,
  .react-photo-album--button:focus {
    outline: 9px double white;
    box-shadow: 0 0 0 6px black;
  }
}

Infinite Scroll

You can use the experimental InfiniteScroll component to implement an infinite scroll feature in your app. The component is currently exported as UnstableInfiniteScroll. Please share your feedback if you have successfully used this component in your project or encountered any issues.

import { UnstableInfiniteScroll as InfiniteScroll } from "react-photo-album/scroll";
PropertyTypeDescription
childrenReactElementPhoto album component. Must be the only child.
fetch(index: number) => Promise<Photo[] | null>
Photo fetcher. Resolve promise with null to indicate end of stream.
photosPhoto[]Initial photos (optional).
retriesnumber

Retry attempts.

Default value: 0

singletonbooleanUse a single photo album component (masonry layout).
fetchRootMarginstring

Fetcher IntersectionObserver root margin setting.

Default value: 800px

offscreenRootMarginstring

Offscreen IntersectionObserver root margin setting.

Default value: 2000px

errorReactNodeMarkup to display when an error occurred.
loadingReactNodeMarkup to display while fetching additional photos.
finishedReactNodeMarkup to display when no more photos are available.

Rows Layout With Infinite Scroll

import { RowsPhotoAlbum } from "react-photo-album";
import { UnstableInfiniteScroll as InfiniteScroll } from "react-photo-album/scroll";
import "react-photo-album/rows.css";

// ...

export default function Gallery() {
  return (
    <InfiniteScroll photos={initialPhotos} fetch={fetchPhotos}>
      <RowsPhotoAlbum
        photos={[]}
        spacing={20}
        componentsProps={{ container: { style: { marginBottom: 20 } } }}
      />
    </InfiniteScroll>
  );
}

Masonry Layout With Infinite Scroll

import { MasonryPhotoAlbum } from "react-photo-album";
import { UnstableInfiniteScroll as InfiniteScroll } from "react-photo-album/scroll";
import "react-photo-album/masonry.css";

// ...

export default function Gallery() {
  return (
    <InfiniteScroll singleton photos={initialPhotos} fetch={fetchPhotos}>
      <MasonryPhotoAlbum
        photos={[]}
        spacing={20}
        componentsProps={{ container: { style: { marginBottom: 20 } } }}
      />
    </InfiniteScroll>
  );
}

Columns Layout With Infinite Scroll

Columns layout is not a good fit for the infinite scroll feature.

Server-Side Rendering (SSR)

By default, React Photo Album produces an empty markup in SSR because the actual container width is usually unknown during server-side rendering. This default behavior causes content layout shift after hydration. As a workaround, you can specify the defaultContainerWidth prop to enable photo album markup rendering in SSR. However, that will likely result in the photo album layout shift once the photo album re-calculates its layout on the client. With this being said, there isn't a perfect solution for SSR, but there are several options to choose from, depending on your use case.

Default Container Width

To render photo album markup on the server, you can specify the defaultContainerWidth value. It is a perfect SSR solution if your photo album has a constant width in all viewports (e.g., an image picker in a fixed-size sidebar). However, if the client-side photo album width doesn't match the defaultContainerWidth, you are almost guaranteed to see a layout shift after hydration.

<RowsPhotoAlbum photos={photos} defaultContainerWidth={800} />

Skeleton

Alternatively, you can provide a fallback skeleton in the skeleton prop that will be rendered in SSR and swapped with the actual photo album markup after hydration. This approach allows you to reserve a blank space on the page for the photo album markup and avoid a flash of the below-the-fold content during hydration. The downside of this approach is that images don't start downloading until after hydration unless you manually add prefetch links to the document <head>.

<RowsPhotoAlbum
  photos={photos}
  skeleton={<div style={{ width: "100%", minHeight: 800 }} />}
/>

Visibility Hidden

Another option is to render the photo album on the server with visibility: hidden. This way, you can avoid a flash of the below-the-fold content and allow the browser to start downloading images before hydration.

<RowsPhotoAlbum
  photos={photos}
  defaultContainerWidth={800}
  componentsProps={(containerWidth) =>
    containerWidth === undefined
      ? {
          container: { style: { visibility: "hidden" } },
        }
      : {}
  }
/>

SSR Component

The ultimate zero-CLS solution requires pre-rendering multiple layouts on the server and displaying the correct one on the client using CSS @container queries. React Photo Album provides an experimental SSR component implementing this approach (the component is currently exported as UnstableSSR). The downside of this approach is the overhead in SSR-generated markup and the hydration of multiple photo album instances on the client (which may be a reasonable compromise if zero CLS is a must-have requirement). You can find a live demo in the Zero CLS SSR example.

import { RowsPhotoAlbum } from "react-photo-album";
import { UnstableSSR as SSR } from "react-photo-album/ssr";
import "react-photo-album/rows.css";

import photos from "./photos";

export default function Gallery() {
  return (
    <SSR breakpoints={[300, 600, 900, 1200]}>
      <RowsPhotoAlbum photos={photos} />
    </SSR>
  );
}
PropertyTypeDescription
breakpointsnumber[]Photo album layout breakpoints.
childrenReactElementPhoto album instance, which must be the only child.
unstyledboolean

If true, do not include the inline stylesheet. Use this option if you are using custom styling solution (e.g., Tailwind CSS)

classNames

{
  container?: string;
  breakpoints?: {
    [key: number]: string;
  }
}

Custom class names for the container and the breakpoint intervals.

Please share your feedback if you have successfully used this component in your project or encountered any issues.

Server Component

React Photo Album provides an experimental server component for rendering static photo albums on the server with zero client-side JS bundle (the component produces pure HTML markup with no client components). The component is currently exported as UnstableServerPhotoAlbum. You can find a live demo in the Server Component example.

import { UnstableServerPhotoAlbum as ServerPhotoAlbum } from "react-photo-album/server";
PropertyTypeDescription
layout"rows" | "columns" | "masonry"Layout type.
breakpointsnumber[]Photo album layout breakpoints.
unstyledboolean
If true, do not include the inline stylesheet.
classNames

{
  container?: string;
  breakpoints?: {
    [key: number]: string;
  }
}

Custom class names for the container and the breakpoint intervals.

In addition to the props listed above, ServerPhotoAlbum supports all relevant props corresponding to the selected layout type except the defaultContainerWidth, onClick and skeleton.

Server Component With Default Styling

import { UnstableServerPhotoAlbum as ServerPhotoAlbum } from "react-photo-album/server";
import "react-photo-album/rows.css";

// ...

export default function Gallery() {
  return (
    <ServerPhotoAlbum
      layout="rows"
      photos={photos}
      breakpoints={[300, 600, 900]}
    />
  );
}

Server Component With Tailwind CSS Styling

Here is an example of custom styling using @tailwindcss/container-queries.

import { UnstableServerPhotoAlbum as ServerPhotoAlbum } from "react-photo-album/server";
import "react-photo-album/rows.css";

// ...

export default function Gallery() {
  return (
    <ServerPhotoAlbum
      unstyled
      layout="rows"
      photos={photos}
      breakpoints={[300, 600, 900]}
      classNames={{
        container: "@container",
        breakpoints: {
          150: "block @[300px]:hidden",
          300: "hidden @[300px]:block @[600px]:hidden",
          600: "hidden @[600px]:block @[900px]:hidden",
          900: "hidden @[900px]:block",
        },
      }}
    />
  );
}

Please share your feedback if you have successfully used this component in your project or encountered any issues.

Previous Versions

Are you looking for documentation for one of the previous versions?