Masonry Gallery
Masonry Gallery displays images with different aspect ratios. Each image keeps its ratio and is placed in the shortest column. As the user scrolls the page, the control appends the next page without introducing a separate internal scrollbar.
Use it for inspiration boards, portfolios, travel photography, media assets, and visual case studies. It is separate from CardSet.
| Control | Primary purpose | Data and layout |
|---|---|---|
| Masonry Gallery | Image-first continuous browsing with mixed ratios | Paged image records and infinite page scrolling |
| CardSet | Business record summaries, statuses, metadata, and actions | A consistent card template with Pagination or Load More |
| Grid | Manually arrange page regions and controls | Fixed 24-column layout; it does not repeat records |
1. Add the control
- Open Page Designer and expand Containers.
- Drag Masonry Gallery onto the page.
- Select it, configure Data Source, and then map the fields.
- Give it a stable name, such as
portfolioGallery.
The design canvas shows representative mixed-ratio cards. Preview loads real data but stops after two pages to prevent continuous loading while editing.
2. Configure the data source
Masonry Gallery supports Model Data Source, Custom API, Media Library, and Object Storage. Model and Custom API return business records; Media Library and Object Storage use a server-side media connector.
2.1 Custom API sample
The basic sample uses:
POST /spark/api/sample/masonry-gallery/images
Configure the Custom URL as follows:
| Property | Value |
|---|---|
| URL | /spark/api/sample/masonry-gallery/images |
| Request Method | POST |
| Rows Path | data.records |
| Total Path | data.total |
| Current Page Path | data.current |
| Page Size Path | data.pageSize |
Select Test & Import to request one page and import these Response Fields:
| Field | Type | Purpose |
|---|---|---|
id | TEXT | Stable unique key |
imageUrl | URL | Image URL |
title | TEXT | Image title |
caption | TEXT | Supporting copy |
badge | TEXT | Category badge |
alt | TEXT | Image alternative text |
width | NUMBER | Original image width |
height | NUMBER | Original image height |
Request body:
{"current": 1, "pageSize": 6}
Recommended response:
{
"success": true,
"data": {
"current": 1,
"pageSize": 6,
"total": 24,
"records": [{
"id": "gallery-1",
"imageUrl": "/spark/api/sample/masonry-gallery/assets/coastal-architecture.jpg",
"title": "Coastal Geometry",
"caption": "Sunlit concrete forms above a quiet blue sea.",
"badge": "Architecture",
"alt": "White coastal architecture above the sea",
"width": 1200,
"height": 1200
}]
}
}
total provides the most reliable end condition. Without it, the gallery stops when a page contains fewer records than Items Per Request. Row Unique Key must remain unique across all pages; duplicate keys are removed.
2.2 Media Library and Object Storage
Create the reusable connection first under App Settings → Integrations → Media Connections:
- Create a credential in Console Secret Management. Use
OBJECT_STORAGEfor OSS/S3/MinIO andMEDIA_PROVIDERfor a media platform. - In Media Connections, select the provider and existing Secret. The form shows only settings required by that provider; automatic official API endpoints stay hidden.
- In the Masonry Gallery Data Source, select Media Library or Object Storage and reference only the Connection. Configure the collection/container, path, filters, and page size.
- Select Test. Test, design preview, and runtime share the same media gateway and response parser.
Currently supported production providers:
| Provider | Secret Management | Media Connection settings |
|---|---|---|
| Cloudinary | MEDIA_PROVIDER; put API Key in API Key and API Secret in API Secret | Cloud Name is required; Root Folder is optional |
| ImageKit | MEDIA_PROVIDER; put Public API Key in API Key and Private API Key in API Secret | Root Folder is optional; Spark uses the official API endpoint automatically |
| Aliyun OSS / AWS S3 / MinIO | OBJECT_STORAGE; use Access Key and Access Secret | Configure endpoint, default bucket, and root prefix as required by the storage service |
Spark Sample Media Library is a credential-free development fixture. Cloudinary and ImageKit are production Media Library connectors; Bynder, AEM Assets, and the complete Spark Media Library are not available yet.
Page JSON never stores secretId, access keys, or temporary signed URLs. The backend normalizes each provider response to records, nextCursor, hasMore, and optional total. Cards use thumbnailUrl; fullscreen preview uses imageUrl.
{
"records": [{
"id": "asset-001",
"imageUrl": "https://cdn.example/full.webp",
"thumbnailUrl": "https://cdn.example/card.webp",
"title": "Summer campaign",
"alt": "Summer campaign launch",
"width": 1600,
"height": 1067
}],
"nextCursor": "opaque-next-cursor",
"hasMore": true,
"total": null
}
The executable sample is in SampleMasonryMediaConnector.java, masonry-gallery-media-connection.json, and masonry-gallery-media-page.json under spark-sample.
3. Map fields
| Property | Recommended mapping | Required |
|---|---|---|
| Row Unique Key | id | Yes |
| Image URL Field | imageUrl | Yes |
| Title Field | title | No |
| Caption Field | caption | No |
| Badge Field | badge | No |
| Alternative Text Field | alt | Recommended |
| Image Width Field | width | Recommended |
| Image Height Field | height | Recommended |
Width and Height are under Advanced. Together they reserve the correct ratio before an image downloads and reduce layout shifts. Without dimensions, the gallery recalculates after the image loads.
Alternative Text should describe the image. It falls back to Title, then to an empty alternative when both are missing.
4. Responsive layout
| Property | Suggested value |
|---|---|
| Desktop Columns | 4 |
| Tablet Columns | 3 |
| Mobile Columns | 2, or 1 for text-heavy cards |
| Spacing | DEFAULT |
| Items Per Request | 6 or 12 |
Columns adapt to page width. Images are not cropped into a common height.
5. Infinite page scrolling
When the user is about one and a half viewports from the current end, the next page begins loading. Only one request is active at a time. Loading, retry, and completion states are shown. Returning to the page shortly afterwards restores loaded records and the previous scroll position.
Do not place the gallery inside a narrow fixed-height container with its own scrollbar.
6. Fullscreen preview and interactions
With Enable Fullscreen Preview, click an image to open the viewer. Use the arrow buttons or arrow keys to move through loaded images, Escape to close, and Enter or Space on a focused card to open it.
Before onClick, the control writes the selected record to Current Record:
record.id
record.imageUrl
record.title
record.badge
A click can both open the viewer and run onClick. If the interaction immediately navigates away, disable Fullscreen Preview.
| Function | Purpose |
|---|---|
reloadGallery | Clear paging state and reload page 1 |
setGalleryData | Replace records directly and stop automatic paging |
7. Request context and backend debugging
Data Source testing, design preview, and runtime use the same request and response logic. Spark automatically sends:
| Header | Values |
|---|---|
X-Spark-Page-Environment | DEV, TEST, PROD |
X-Spark-Page-Render-Mode | DESIGN_PREVIEW, RUNTIME |
Do not add these headers manually. A backend may return a smaller development dataset for quick preview and the complete dataset at runtime. The sample controller uses the shared header constants and parser:
@PostMapping
public ApiResult<ProTablePageResult<MasonryGalleryImageDTO>> queryByPost(
@RequestBody(required = false) MasonryGalleryQueryRequest request,
@RequestHeader(value = SparkPageRequestHeaders.ENVIRONMENT, required = false) String environment,
@RequestHeader(value = SparkPageRequestHeaders.RENDER_MODE, required = false) String renderMode) {
return SparkApiResultHelper.success(
Locale.ENGLISH,
galleryService.query(request,
SparkPageRequestHeaders.resolve(environment, renderMode)),
"SPARK-SAMPLE-MASONRY-GALLERY-0001");
}
The service chooses the dataset size from the render mode:
int total = requestContext != null
&& requestContext.getRenderMode() == SparkPageRenderMode.DESIGN_PREVIEW
? 12
: 24;
The working Java endpoint, service, page JSON, and image assets are in spark-sample under gallery/, resources/samples/masonry-gallery-page.json, and resources/static/sample/masonry-gallery/. See Page request context and backend debugging.
8. Performance checklist
- Serve display-sized images instead of multi-megabyte originals.
- Prefer WebP, AVIF, or compressed JPEG with CDN or same-origin caching.
- Return stable
id,width,height, and meaningfulaltfields. - Start with 6–20 records per request and use stable backend sorting.
- Treat image failures separately from data API failures; one broken image does not stop paging.
9. Troubleshooting
- The same images repeat: apply
currentandpageSizeon the backend and verify all response paths. - Scrolling no longer loads: check
total, short pages, and whether a failed request is waiting for Retry. - The layout jumps: return and map
widthandheight. - Images show placeholders: verify Image URL Field, authentication, and CORS.
- You need record actions and rich business metadata: use CardSet; Masonry Gallery intentionally has no Card Actions.