A React component library for creating immersive IIIF exhibition experiences. Built originally for the Delft University of Technology Heritage collections by Digirati.

npm install exhibition-viewer
The library provides four distinct viewer variations, each suited to different presentation needs:
| Variation | Component | Best For |
|---|
| Delft Exhibition | DelftExhibition | Grid-based layouts with scrollable content blocks |
| Scroll Exhibition | ScrollExhibition | Immersive, full-screen scrolling experiences |
| Delft Slideshow | DelftSlideshow | Carousel-based presentations |
| Presentation Mode | DelftPresentation | Full-screen presentation/kiosk displays |
The library requires CSS to be imported. Choose one of the following approaches:
// Option 1: Import the bundled CSS (recommended)
import "exhibition-viewer/dist/lib.css";
// Option 2: If using Tailwind, you can also import the base styles
import "exhibition-viewer/dist/index.css";
A grid-based exhibition viewer with support for presentation mode dialog.
import { DelftExhibition } from "exhibition-viewer";
import "exhibition-viewer/dist/lib.css";
function MyExhibition() {
return (
<DelftExhibition
manifest="https://example.org/iiif/manifest.json"
language="en"
options={{
cutCorners: true, // Enable cut-corner visual style
fullTitleBar: false, // Use compact title bar
hideTitle: false, // Show exhibition title
hideTableOfContents: false, // Show table of contents navigation
hideTitleCard: false, // Show title card
disablePresentation: false, // Enable presentation mode button
fullWidthGrid: false, // Force full-width grid items
coverImages: false, // Use cover fit for images
alternativeImageMode: true, // Alternative image rendering
transitionScale: false, // Enable scale transitions
imageInfoIcon: false, // Show image info icons
}}
content={{
exhibition: "Exhibition",
tableOfContents: "Table of Contents",
}}
viewObjectLinks={[]}
/>
);
}
An immersive scrolling experience with sticky images and floating/split text overlays.
import { ScrollExhibition } from "exhibition-viewer";
import "exhibition-viewer/dist/lib.css";
function MyScrollExhibition() {
return (
<ScrollExhibition
manifest="https://example.org/iiif/manifest.json"
language="en"
showTableOfContents={true}
options={{
titleBlock: {
fullHeight: true, // Title block takes full viewport height
},
}}
viewObjectLinks={[]}
/>
);
}
A carousel-based presentation with manual navigation controls.
import { DelftSlideshow } from "exhibition-viewer";
import "exhibition-viewer/dist/lib.css";
function MySlideshowViewer() {
return (
<div style={{ height: "600px", width: "800px" }}>
<DelftSlideshow
manifest={manifestObject} // Requires pre-loaded manifest object
language="en"
options={{
alternativeImageMode: true,
transitionScale: false,
imageInfoIcon: false,
coverImages: false,
}}
viewObjectLinks={[]}
/>
</div>
);
}
Full-screen presentation mode, typically launched from DelftExhibition but can be used standalone.
import { DelftPresentation } from "exhibition-viewer";
import "exhibition-viewer/dist/lib.css";
function MyPresentation() {
return (
<div style={{ height: "100vh" }}>
<DelftPresentation
manifest="https://example.org/iiif/manifest.json"
language="en"
options={{
cutCorners: true,
autoPlay: true, // Auto-advance slides
isFloating: false, // Enable floating text panels
floatingPosition: "bottom-left", // Position: top-left, top-right, bottom-left, bottom-right
}}
viewObjectLinks={[]}
/>
</div>
);
}
All viewers accept either a manifest URL (string) or a pre-loaded manifest object:
// Using a URL (manifest will be fetched automatically)
<DelftExhibition manifest="https://example.org/iiif/manifest.json" />;
// Using a pre-loaded manifest object
const manifest = await fetch("https://example.org/iiif/manifest.json").then(
(r) => r.json(),
);
<DelftExhibition manifest={manifest} skipLoadManifest />;
The Exhibition Viewer can be embedded via iframe using the hosted preview at https://preview.exhibitionviewer.org/.
<iframe
src="https://preview.exhibitionviewer.org/?manifest=YOUR_MANIFEST_URL"
width="100%"
height="800"
frameborder="0"
allowfullscreen
></iframe>
| Parameter | Values | Description |
|---|
manifest | URL | Required. URL to the IIIF manifest |
type | presentation, scroll, slideshow | Viewer variation (default: exhibition grid) |
cut-corners | true, false | Enable/disable cut-corner style (default: true) |
full-title-bar | true, false | Use full title bar (default: false) |
floating | true, false | Enable floating text panels (presentation mode) |
floating-position | top-left, top-right, bottom-left, bottom-right | Floating panel position |
Standard Exhibition Grid:
<iframe
src="https://preview.exhibitionviewer.org/?manifest=https://example.org/manifest.json"
width="100%"
height="800"
></iframe>
Scroll Exhibition:
<iframe
src="https://preview.exhibitionviewer.org/?manifest=https://example.org/manifest.json&type=scroll"
width="100%"
height="100vh"
></iframe>
Presentation Mode:
<iframe
src="https://preview.exhibitionviewer.org/?manifest=https://example.org/manifest.json&type=presentation"
width="100%"
height="600"
></iframe>
Presentation with Floating Panels:
<iframe
src="https://preview.exhibitionviewer.org/?manifest=https://example.org/manifest.json&type=presentation&floating=true&floating-position=bottom-right"
width="100%"
height="600"
></iframe>
The viewer supports CSS custom properties for theming. Override these variables in your CSS:
:root {
/* Base colors */
--delft-bg-primary: rgb(229 231 235);
--delft-bg-secondary: #fff;
--delft-bg-overlay: rgba(0, 0, 0, 0.3);
/* Text colors */
--delft-text-primary: #fff;
--delft-text-secondary: #000;
--delft-image-caption: #fff;
--delft-annotation-selected: rgb(250, 204, 21);
/* UI elements */
--delft-control-bar: #6d6e70;
--delft-control-bar-border: #5a5b5d;
--delft-control-hover: rgba(0, 0, 0, 0.1);
--delft-progress-bar: #fff;
/* Close button */
--delft-close-background: #000;
--delft-close-background-hover: #373737;
--delft-close-text: #fff;
/* Title elements */
--delft-title-card: rgb(250 204 21);
--delft-title-card-text: #000;
/* Info blocks */
--delft-info-block: #000;
--delft-info-block-text: #fff;
/* Viewer elements */
--delft-viewer-background: #373737;
--delft-title-transform: uppercase;
}
.exv-scroll {
/* Title section */
--exv-scroll-title-background: #fff;
--exv-scroll-title-color: #444;
/* Annotation overlays */
--exv-scroll-annotation-background: #fff;
--exv-scroll-annotation-color: #333;
--exv-scroll-annotation-radius: 0px;
--exv-scroll-annotation-max-width: 30em;
/* Info blocks */
--exv-scroll-info-block-background: #fff;
--exv-scroll-info-block-color: #444;
}
Control how canvases are displayed using IIIF behavior values.
{
"id": "https://example.org/canvas/1",
"type": "Canvas",
"behavior": ["h-8", "w-6", "left"],
"label": { "en": ["My Canvas"] },
"summary": { "en": ["Description text"] }
}
Common behaviors:
h-1 to h-12 - Height/row span (Delft Exhibition)
w-1 to w-12 - Width/column span (Delft Exhibition)
left, right, top, bottom - Text panel position
floating, float-top-left, etc. - Floating panel mode
backdrop-light, backdrop-dark - Overlay appearance
cover, image-cover - Image display mode
For more granular control, import individual components from the library export:
import {
// Main viewers
DelftExhibition,
DelftPresentation,
DelftSlideshow,
ScrollExhibition,
// Individual blocks
ImageBlock,
InfoBlock,
MediaBlock,
// Helpers
getClassName,
getFloatingFromBehaviours,
getScrollLayoutConfig,
} from "exhibition-viewer/library";
If you’re using Tailwind CSS, you can import the Tailwind configuration:
// tailwind.config.js
import exhibitionTailwind from "exhibition-viewer/tailwind";
export default {
// ... your config
presets: [exhibitionTailwind],
};
Explore the viewer variations with sample exhibitions at:
https://preview.exhibitionviewer.org/
MIT © Digirati