diff --git a/README.md b/README.md
index 3d2b64b..1533fce 100644
--- a/README.md
+++ b/README.md
@@ -171,7 +171,9 @@ enable server-side rendering, specify the `defaultContainerWidth` prop.
Otherwise, React Photo Album produces an empty markup on the server and renders
on the client only after hydration. Please note that unless your photo album is
of constant width that always matches the `defaultContainerWidth` value, you
-will most likely see a layout shift immediately after hydration.
+will most likely see a layout shift immediately after hydration. 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.
## Credits
diff --git a/docs/documentation.md b/docs/documentation.md
index a7fe2a7..cb72311 100644
--- a/docs/documentation.md
+++ b/docs/documentation.md
@@ -85,16 +85,6 @@ The following props are applicable to all three layouts.
function |
Photo click callback. See [Click Handler](#ClickHandler) for details. |
-
- defaultContainerWidth |
- number |
-
- The default container width in server-side rendering (SSR). This prop is
- required to enable server-side rendering. If absent,
- [React Photo Album](/) produces an empty markup on the server and
- renders on the client only after hydration.
- |
-
sizes |
object |
@@ -127,6 +117,22 @@ The following props are applicable to all three layouts.
Custom render functions. See [Render Functions](#RenderFunctions) for details.
+
+ defaultContainerWidth |
+ number |
+
+ The default container width in server-side rendering (SSR).
+ See [Default Container Width](#Server-SideRendering(SSR)_DefaultContainerWidth) for details.
+ |
+
+
+ skeleton |
+ ReactNode |
+
+ Fallback skeleton in SSR.
+ See [Skeleton](#Server-SideRendering(SSR)_Skeleton) for details.
+ |
+
@@ -679,6 +685,37 @@ the content container padding and the left-hand side navigation menu:
/>
```
+## Server-Side Rendering (SSR)
+
+By default, [React Photo Album](/) produces an empty markup on the server, but
+several alternative solutions are available.
+
+### 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.
+
+```tsx
+
+```
+
+### 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 for the photo album
+markup and avoid a flash of below-the-fold content during hydration.
+
+```tsx
+}
+/>
+```
+
## Previous Versions
Are you looking for documentation for one of the previous versions?
diff --git a/src/client/columns/ColumnsPhotoAlbum.tsx b/src/client/columns/ColumnsPhotoAlbum.tsx
index 534e656..b3ddff3 100644
--- a/src/client/columns/ColumnsPhotoAlbum.tsx
+++ b/src/client/columns/ColumnsPhotoAlbum.tsx
@@ -22,6 +22,7 @@ export default function ColumnsPhotoAlbum({
sizes,
breakpoints,
defaultContainerWidth,
+ skeleton,
...restProps
}: ColumnsPhotoAlbumProps) {
const { containerRef, containerWidth } = useContainerWidth(breakpoints, defaultContainerWidth);
@@ -37,6 +38,10 @@ export default function ColumnsPhotoAlbum({
);
return (
-
+
);
}
diff --git a/src/client/masonry/MasonryPhotoAlbum.tsx b/src/client/masonry/MasonryPhotoAlbum.tsx
index 05ea3bd..b30f395 100644
--- a/src/client/masonry/MasonryPhotoAlbum.tsx
+++ b/src/client/masonry/MasonryPhotoAlbum.tsx
@@ -22,6 +22,7 @@ export default function MasonryPhotoAlbum({
sizes,
breakpoints,
defaultContainerWidth,
+ skeleton,
...restProps
}: MasonryPhotoAlbumProps) {
const { containerRef, containerWidth } = useContainerWidth(breakpoints, defaultContainerWidth);
@@ -37,6 +38,10 @@ export default function MasonryPhotoAlbum({
);
return (
-
+
);
}
diff --git a/src/client/rows/RowsPhotoAlbum.tsx b/src/client/rows/RowsPhotoAlbum.tsx
index b473591..6ac6724 100644
--- a/src/client/rows/RowsPhotoAlbum.tsx
+++ b/src/client/rows/RowsPhotoAlbum.tsx
@@ -35,6 +35,7 @@ export default function RowsPhotoAlbum({
sizes,
breakpoints,
defaultContainerWidth,
+ skeleton,
...rest
}: RowsPhotoAlbumProps) {
const { containerRef, containerWidth } = useContainerWidth(breakpoints, defaultContainerWidth);
@@ -64,5 +65,11 @@ export default function RowsPhotoAlbum({
}
}
- return ;
+ return (
+
+ );
}
diff --git a/src/core/static/StaticPhotoAlbum.tsx b/src/core/static/StaticPhotoAlbum.tsx
index f464e0a..4dd1882 100644
--- a/src/core/static/StaticPhotoAlbum.tsx
+++ b/src/core/static/StaticPhotoAlbum.tsx
@@ -6,7 +6,10 @@ import PhotoComponent from "./PhotoComponent";
import { srcSetAndSizes, unwrap } from "../utils";
import { CommonPhotoAlbumProps, ComponentsProps, LayoutModel, Photo, Render } from "../../types";
-type StaticPhotoAlbumProps = Pick, "sizes" | "onClick"> & {
+type StaticPhotoAlbumProps = Pick<
+ CommonPhotoAlbumProps,
+ "sizes" | "onClick" | "skeleton"
+> & {
layout?: string;
model?: LayoutModel;
render?: Render;
@@ -18,6 +21,7 @@ export default forwardRef(function StaticPhotoAlbum(
layout,
sizes,
model,
+ skeleton,
onClick: onClickCallback,
render: { container, track, photo: renderPhoto, ...restRender } = {},
componentsProps: {
@@ -100,6 +104,8 @@ export default forwardRef(function StaticPhotoAlbum(
);
})}
+
+ {containerWidth === undefined && skeleton}
);
}) as (
diff --git a/src/types.ts b/src/types.ts
index 748116c..9c94fe5 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -46,6 +46,8 @@ export interface CommonPhotoAlbumProps {
render?: ResponsiveParameter>;
/** Additional HTML attributes to be passed to the rendered elements. */
componentsProps?: ComponentsProps | ((containerWidth?: number) => ComponentsProps);
+ /** Fallback skeleton in SSR. */
+ skeleton?: React.ReactNode;
}
/** Rows photo album props */