Skip to Content

Viewer

A ready-made viewer for IIIF Manifests and Collections, laid out by a template.

import Viewer from "@nulib/clover";
import { Default } from "@nulib/clover/templates";
import "@nulib/clover/styles.css";
 
<Viewer
  iiifContent="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json"
  template={}
  items=
  panelLayout=
  header={}
/>;
<script type="module">
  import "@nulib/clover/web-components";
</script>
 
<clover-viewer
  iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json"
  template=
  items=
  panel-layout=
  header="">
</clover-viewer>
import Viewer from "@nulib/clover";
import { Document } from "@nulib/clover/templates";
import "@nulib/clover/styles.css";
import "@nulib/clover/templates.css";
 
<Viewer
  iiifContent="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json"
  template={}
  defaultSidebarOpen={}
  defaultPanelOpen={}
  panelLayout=
/>;
<script type="module">
  import "@nulib/clover/web-components";
</script>
 
<clover-viewer
  iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json"
  template=
  sidebar-open=""
  panel-open=""
  panel-layout=>
</clover-viewer>
import Viewer from "@nulib/clover";
import { Gallery } from "@nulib/clover/templates";
import "@nulib/clover/styles.css";
import "@nulib/clover/templates.css";
 
<Viewer
  iiifContent="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json"
  template={}
  size="rem"
/>;
<script type="module">
  import "@nulib/clover/web-components";
</script>
 
<clover-viewer
  iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json"
  template=
  size="rem">
</clover-viewer>
Template

API reference

Viewer<clover-viewer> is the package’s default export. It draws its template, and hands every other prop to it: the template’s own, below, and those of Root<clover-root>, which every template is built on.

Viewer

PropTypeDefaultDescription
iiifContentstring | objectRequiredWhat to show: a URL, or a IIIF resource or content state as an object.
templateTemplateDefault, on demandThe layout: one from @nulib/clover/templates, or your own.
canvasIdstring—The Canvas to show, controlled: moving asks onCanvasChange to change it.
defaultCanvasIdstring—The Canvas to start on. A content state or the Manifest’s start still wins.
onCanvasChange(canvasId: string) => void—The reader moved to a Canvas.
manifestIdstring—In a Collection, the Manifest to show, controlled, with onManifestChange.
defaultManifestIdstring—In a Collection, the Manifest to open first.
onManifestChange(manifestId: string) => void—In a Collection, the reader moved, or asks to move, to a Manifest.
searchQuerystring—A Content Search query, run once the Manifest shows and whenever it changes.
annotationsstring | object | array—Annotation pages or collections to show beside the Manifest’s own.
annotationPages"view" | "all""view"Whose referenced annotation pages to fetch: the view’s, or every Canvas’s at once.
onContentStateChange(state: { json, encoded }) => void—Where the reader is, as a content state, whenever it settles.
onStatusChange(status, error) => void—The resource is "loading", "ready", or met an "error".
request{ credentials, headers, fetch }—Credentials, headers and a fetch of your own, for every request.
languagestringThe reader’sThe language to read IIIF text in.
renderersBodyRenderer[]—Components of your own for views Clover does not draw.

iiifContent can be:

  • A Manifest, shown from its start Canvas, or its first.
  • A Collection. Its first Manifest is shown, or the one manifestId names, and its members are listed in the panel’s Collection section. Paged Collections are followed through next, and a nested Collection is entered.
  • A Canvas, shown on its own. Where it is partOf a Manifest, About names it, with a button that opens the Manifest at this Canvas.
  • A content state, as JSON or base64url. It opens at the Canvas it points to, framing its region or seeking to its time. One that carries a body shows that note on the Canvas, as an annotation.
  • Annotations: an AnnotationPage or AnnotationCollection made apart from any Manifest. The Viewer opens the first one’s Manifest at its Canvas.
  • An object of any of these. It is known by its id, so a new object with the same id does not reload.

Everything is upgraded to IIIF Presentation 4 as it loads, so every part reads one shape. toPresentation4() does the same for data you hold yourself.

Default

The Viewer’s own layout, drawn when it is given no template: the title over the Body, the thumbnails under it, and the information panel beside them, as in Clover 3.x.

PropTypeDefaultDescription
headerbooleantrueThe title and provider over the Body.
headingLevelnumber2The title’s level; the panel’s sections are a level below.
panelbooleantrueThe information panel and its toggle.
downloadbooleantrueThe downloads in the panel’s About, where there is a rendering.
languagesbooleantrueThe languages to read the resource in, beside the panel’s About heading, where it comes in several; false leaves the choice out.
defaultPanelOpenbooleantrueWhether the panel starts open beside the Body; over it, closed.
panelLayout"accordion" | "tabs""accordion"The panel’s sections as stacked boxes, or as tabs.
items"slider" | "list" | "grid" | false"slider"The thumbnails under the Body, or none.
storageKeystring | false"clover-template-default"Where the panel’s width is remembered; false forgets it.
fillbooleanfalseFill the box it sits in; the Body takes what the rest leave.

Document

Reads like a PDF viewer: a toolbar with the title and Download, page thumbnails down the left, the pages down one column, and the information panel sliding out on the right.

PropTypeDefaultDescription
defaultSidebarOpenbooleantrueWhether the page thumbnails start shown; over the page, not.
defaultPanelOpenbooleanfalseWhether the panel starts open beside the page; over it, closed.
panelLayout"accordion" | "tabs""accordion"The panel’s sections as stacked boxes, or as tabs.
downloadbooleantrueDownload’s links in the toolbar, where there is a rendering.
languagesbooleantrueThe languages to read the resource in, beside the panel’s About heading, where it comes in several; false leaves the choice out.
headingLevelnumber2The title’s level; the panel’s sections are a level below.
storageKeystring | false"clover-template-document"Where the thumbnails’ width is remembered; false forgets it.

A grid of the views, a spread one cell, that opens one in a lightbox to pan, zoom and page through.

PropTypeDefaultDescription
sizestring10remA cell’s width (sets --clover-items-size).
headingLevelnumber2The title’s level; the lightbox’s title is one below.

<clover-viewer>

Clover's Viewer, in one of its templates: Default (the title over the Body, the thumbnails under it, the information panel beside them), Document (a PDF reader's pages, thumbnails beside them) or Gallery (a grid, each item opening in a lightbox). Each template loads when used, its styles with it.

NameTypeDefaultDescription
template"default" | "document" | "gallery"defaultThe layout: default, document or gallery. Default's comes with the element; another loads the first time one is shown.
iiif-contentstring—What to show: a URL, or a content state.
canvas-idstring—The Canvas to open on.
manifest-idstring—In a Collection, the Manifest to open first. The reader moves on from it.
languagestring—The language to read IIIF text in.
search-querystring—A Content Search to run as the Manifest opens.
annotationsstring (a URL or JSON)—Annotations to show beside the Manifest's own: a URL, or JSON (a list of URLs, or an AnnotationPage).
heading-levelnumber2The title's level, 2 or more, to fit the page's outline. The panel's sections are a level below.
items"slider" | "list" | "grid" | "false"sliderDefault: the thumbnails, as a slider, list or grid, or "false" for none.
headerbooleantrueDefault: "false" leaves out the title and provider over the Body.
panelbooleantrueDefault: "false" leaves out the information panel.
downloadbooleantrueDefault and Document: "false" leaves out the downloads: Default's from a Download button beside its panel's About heading, Document's in its toolbar (its panel's About offers none).
languagesbooleantrueDefault and Document: where what the resource says of itself comes in several languages, the panel's About heading offers them, and the whole Viewer reads in the one chosen. "false" leaves the choice out.
panel-openboolean—Default and Document: whether the information panel starts open beside the page. Default's does, by default; Document's starts shut. Where it would slide over the page (a phone), it starts shut.
panel-layout"accordion" | "tabs"accordionDefault and Document: the panel's sections as stacked boxes, each opened by its heading, or as tabs.
sidebar-openboolean—Document: whether the page thumbnails start shown beside the page, as they do by default. Where they would float over the page (a phone), they start put away.
storage-keystring—Default and Document: where the width the reader gives a pane is remembered (localStorage); "false" forgets it.
fillbooleanfalseDefault: as tall as the box it is in, the Body taking what the header and the thumbnails leave.
sizestring10remGallery: the size of a cell in the grid.
.iiifContentstring | Record<string, unknown> | null—As a property: What to show, as the attribute takes it, or a IIIF resource as an object.
.annotationsstring | Record<string, unknown> | Array<string | Record<string, unknown>> | null—As a property: Annotations to show beside the Manifest's own, as URLs or objects.
.request{ credentials?: RequestCredentials; headers?: Record<string, string>; fetch?: typeof fetch } | null—Credentials, headers and a fetch of your own, for every request.
.renderersCloverRenderer[] | null—Renderers of yours, for its Body: each claims views (canvasId, format or match) and draws one into a box of its own (render), returning what undoes it.
clover-canvas-change{ canvasId: string }—An event. The reader moved to a Canvas.
clover-manifest-change{ manifestId: string }—An event. In a Collection, the reader moved to another Manifest.
clover-status-change{ status: "loading" | "ready" | "error"; error: Error | null }—An event. What it was given is loading, has loaded, or failed to.
clover-content-state-change{ json: Record<string, unknown>; encoded: string }—An event. Where the reader is, as a IIIF content state, each time it settles.

Examples

Templates

Pass a template from @nulib/clover/templates as template. A page loads only the template it passes, and the parts that template uses: a Gallery loads no information panel.

Given none, the Viewer loads Default the first time it draws, as a chunk of its own: one request before the Manifest loads, and nothing drawn on the server. To have it up front, pass it like any other.

import { Default } from "@nulib/clover/templates"; <Viewer iiifContent={url} template={Default} />;
<script type="module"> import "@nulib/clover/web-components"; </script> <clover-viewer iiif-content="https://iiif.io/api/cookbook/recipe/0009-book-1/manifest.json" template="default"></clover-viewer>

Every template is built only from parts @nulib/clover exports. To change one, copy its source, in packages/clover/src/templates/, into your app. To start from nothing, see Composing layouts.

Panel layout

Use panelLayoutpanel-layout to lay the information panel’s sections out as tabs rather than stacked boxes.

panelLayout
<Viewer iiifContent={url} panelLayout="accordion" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" panel-layout="accordion"></clover-viewer>
<Viewer iiifContent={url} panelLayout="tabs" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" panel-layout="tabs"></clover-viewer>

Thumbnails

Use items to lay the thumbnails under the Body out as a slider, a list or a grid, or false to leave them out.

items
<Viewer iiifContent={url} items="slider" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" items="slider"></clover-viewer>
<Viewer iiifContent={url} items="list" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" items="list"></clover-viewer>
<Viewer iiifContent={url} items="grid" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" items="grid"></clover-viewer>
<Viewer iiifContent={url} items={false} />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" items="false"></clover-viewer>

Header and panel

Use header={false}header="false" to leave out the title over the Body, and panel={false}panel="false" to leave out the information panel and its toggle. defaultPanelOpenpanel-open starts the panel open or closed beside the Body. On a phone, where it would cover the Body, it starts closed whatever you pass, and the reader opens it with its toggle.

<Viewer iiifContent="https://api.dc.library.northwestern.edu/api/v2/works/d8fb6329-f7fb-48bf-8bb7-70a64ded5a15?as=iiif" header={false} panel={false} />
<clover-viewer iiif-content="https://api.dc.library.northwestern.edu/api/v2/works/d8fb6329-f7fb-48bf-8bb7-70a64ded5a15?as=iiif" header="false" panel="false"></clover-viewer>

Fill

Use fill to have the Viewer fill a box of a set height, the window under a page’s own header, say. The Body takes what the title and the thumbnails leave, never less than 16rem; a shorter box scrolls.

<div style={{ height: "34rem" }}> <Viewer iiifContent="https://api.dc.library.northwestern.edu/api/v2/works/0e66d6d8-5dc5-4686-b056-d18682c79ebf?as=iiif" fill /> </div>
<div style="height: 34rem"> <clover-viewer iiif-content="https://api.dc.library.northwestern.edu/api/v2/works/0e66d6d8-5dc5-4686-b056-d18682c79ebf?as=iiif" fill></clover-viewer> </div>

Heading level

Use headingLevelheading-level to fit the Viewer into the page’s outline. Its title is an h2, and the panel’s sections h3s. Under a heading of your own, pass the level below it: the Viewers on this page sit under h3s, and pass 4.

<h3>The catalog</h3> <Viewer iiifContent={url} headingLevel={4} />
<h3>The catalog</h3> <clover-viewer iiif-content="https://iiif.io/api/cookbook/recipe/0009-book-1/manifest.json" heading-level="4"></clover-viewer>

With header={false}header="false" there is no title, and the panel’s sections take its place, at headingLevelheading-level itself.

Use size to set the width of Gallery’s cells.

<Viewer iiifContent={url} template={Gallery} size="8rem" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" template="gallery" size="8rem"></clover-viewer>

Collection

Give a Collection as iiifContentiiif-content, and the Viewer shows its first Manifest and lists its members in the panel’s Collection section. Choosing one shows it. manifestIdmanifest-id with onManifestChangeclover-manifest-change makes the Manifest controlled, as canvasIdcanvas-id does the Canvas.

<Viewer iiifContent="https://api.dc.library.northwestern.edu/api/v2/collections/c373ecd2-2c45-45f2-9f9e-52dc244870bd?as=iiif" />
<clover-viewer iiif-content="https://api.dc.library.northwestern.edu/api/v2/collections/c373ecd2-2c45-45f2-9f9e-52dc244870bd?as=iiif"></clover-viewer>

Opening at a place

Give a content state as iiifContentiiif-content to open at a Canvas, framing a region of it (xywh=) or seeking to a time (t=). This one opens the catalog at Page 4, framed on the plate its “Then” tags.

import Viewer, { encodeContentState } from "@nulib/clover"; const state = { type: "Annotation", motivation: ["contentState"], target: { type: "SpecificResource", source: { id: "https://clover.iiif.library.northwestern.edu/mocks/paged/canvas/5", type: "Canvas", partOf: [ { id: "https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json", type: "Manifest", }, ], }, selector: { type: "FragmentSelector", value: "xywh=2705,66,1419,1518" }, }, }; <Viewer iiifContent={encodeContentState(state)} />;
<clover-viewer></clover-viewer> <script type="module"> document.querySelector("clover-viewer").iiifContent = { type: "Annotation", motivation: ["contentState"], target: { type: "SpecificResource", source: { id: "https://clover.iiif.library.northwestern.edu/mocks/paged/canvas/5", type: "Canvas", partOf: [ { id: "https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json", type: "Manifest", }, ], }, selector: { type: "FragmentSelector", value: "xywh=2705,66,1419,1518" }, }, }; </script>

onContentStateChangeclover-content-state-change hands you a content state whenever the reader settles: the Canvas and region, or the moment in a recording, they are at. Put it in a link to share it.

<Viewer iiifContent={url} onContentStateChange={({ encoded }) => history.replaceState(null, "", `?iiif-content=${encoded}`) } />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" ></clover-viewer> <script type="module"> document .querySelector("clover-viewer") .addEventListener("clover-content-state-change", ({ detail }) => history.replaceState(null, "", `?iiif-content=${detail.encoded}`), ); </script>

Annotations

Annotations on the Manifest’s Canvases (comments, tags, transcriptions) are listed in the panel’s Annotations section and drawn on the Canvas. Picking one frames it, moving to its Canvas first. This catalog has each plate’s “Then” and “Now” tagged where they are.

<Viewer iiifContent="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json"></clover-viewer>

Use annotations to show pages of your own beside the Manifest’s.

<Viewer iiifContent={url} annotations={annotationPageUrl} />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" annotations="https://example.org/annotation-page.json" ></clover-viewer>

A Manifest with a IIIF Content Search service, version 1 or 2, gets a Search section: its hits listed by page and drawn on the Canvas, the first gone to as they arrive. Use searchQuerysearch-query to search as it opens. This is the homepage’s catalog, its words read by machine and searchable (mock), searched for “steel”.

<Viewer iiifContent="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" searchQuery="steel" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" search-query="steel"></clover-viewer>

Video and sound

Video and sound play in the Canvas’s own controls. Their captions are drawn over the picture and listed in the Transcript section, chapters come from the Manifest’s Ranges, and an annotation can follow a region through time (xywh=…&t=…). In this example, Clover plays a film whose annotations each follow a region as it moves, with its captions and chapters beside it.

<Viewer iiifContent="https://clover.iiif.library.northwestern.edu/mocks/rudolf-hess-nuremberg/manifest.json" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/rudolf-hess-nuremberg/manifest.json"></clover-viewer>

Animation

A Canvas whose images are timed, one per frame, plays as an animation, with a frame bar to step, scrub and pause it. auto-advance plays it on arrival and repeat loops it. Nothing plays for a reader who asks for reduced motion.

<Viewer iiifContent="https://nulib-ds.github.io/muybridge/iiif/plate-number-700-buffalo-galloping.json" />
<clover-viewer iiif-content="https://nulib-ds.github.io/muybridge/iiif/plate-number-700-buffalo-galloping.json"></clover-viewer>

Places

A Manifest with a navPlace.navPlace, or a Canvas with a georeference, gets a Map section: its places, or the sheet laid over the ground. See Map.

<Viewer iiifContent="https://clover.iiif.library.northwestern.edu/mocks/georeference/manifest.json" />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/georeference/manifest.json"></clover-viewer>

Requests

Use request.request for every request the Viewer makes: the Manifest, annotation pages, info.json, image tiles, captions and HLS segments.

<Viewer iiifContent={url} request={{ credentials: "include", headers: { Authorization: `Bearer ${token}` }, }} />
<clover-viewer iiif-content="https://clover.iiif.library.northwestern.edu/mocks/paged/manifest.json" ></clover-viewer> <script type="module"> document.querySelector("clover-viewer").request = { credentials: "include", headers: { Authorization: `Bearer ${token}` }, }; </script>

A <video> element cannot send headers, so plain video and sound get only the credentials. So does an image served without CORS, which Clover shows as an <img>.

Renderers

Use renderers.renderers to draw a view with a component of your own: one Clover does not draw, a PDF say, or one you want drawn otherwise. See Renderers of your own.

Styles

Import @nulib/clover/styles.css once, and @nulib/clover/templates.css for Document and Gallery. Size the Body with --clover-body-height.

.catalog { --clover-body-height: 32rem; }

The rest is in Styling.

From Clover 3.x

Clover 3.x’s Viewer, @samvera/clover-iiif, is documented at Clover 3.x › Viewer. Its iiifContentSearchQuery is searchQuerysearch-query, and its customDisplays are renderers.renderers. Its options.informationPanel is set out on Panel.