diff --git a/pages/docs/rendering.mdx b/pages/docs/rendering.mdx
new file mode 100644
index 000000000..8f410482a
--- /dev/null
+++ b/pages/docs/rendering.mdx
@@ -0,0 +1,144 @@
+---
+title: Rendering
+---
+
+import IIIFBadge from "docs/components/IIIFBadge";
+import { Rendering } from "src/components/Primitives";
+import { Tabs, Tab } from "nextra/components";
+
+# Rendering
+
+The Rendering component is used to display a list of alternate formats related to a resource. Whereas SeeAlso is used
+to link to a machine-readable resource such as metadata, the Rendering component alerts users that the resource is
+available in another format such as PDF or ePub or has a related format. Because of the wide variety of formats that
+resources can be available in, the Rendering component is flexible and be of any media type and contain any type of
+data.
+
+
+
+
+
+ ```jsx
+
+ ```
+
+
+
+
+
+ ```html
+
+ ```
+
+
+
+## Usage
+
+### React
+
+```jsx
+import { Rendering } from "@samvera/clover-iiif/primitives";
+
+const CustomRendering = ({ rendering }) => {
+ return ;
+};
+
+export default CustomRendering;
+```
+
+## API Reference
+
+| Prop | Type | Default | Required |
+| ----------- | ------------------------------------------------------------ | ------- | -------- |
+| `as` | `ol`, `ul` | `ul` | -- |
+| `rendering` | [rendering](https://iiif.io/api/presentation/3.0/#rendering) | -- | **Yes** |
+| `className` | `string`, `undefined` | -- | -- |
+| `style` | `CSSProperties`, `undefined` | -- | -- |
+| `lang` | `string`, `undefined` | -- | -- |
+| `title` | `string`, `undefined` | -- | -- |
+| `data-*` | `string`, `undefined` | -- | -- |
+| `aria-*` | `AriaAttributes`, `undefined` | -- | -- |
+
+### Custom Element
+
+The `Rendering` component can be rendered as either `ol` or `ul` elements. The default is `ul`.
+
+
+
+ ```jsx
+
+ ```
+
+
+
+
+
+ ```html
+
+ -
+
+ PDF version
+
+
+
+ ```
+
+
diff --git a/pages/index.mdx b/pages/index.mdx
index 9e41cc42a..8eda9fa69 100644
--- a/pages/index.mdx
+++ b/pages/index.mdx
@@ -10,6 +10,7 @@ import {
Label,
Metadata,
PartOf,
+ Rendering,
RequiredStatement,
SeeAlso,
Summary,
@@ -306,6 +307,32 @@ import "swiper/css/pagination";
```
+
+ }
+ href="docs/rendering"
+ >
+ ```jsx
+
+ ```
+
= (props) => {
+ const { as, rendering } = props;
+
+ /**
+ * Create attributes and remove React props
+ */
+ const remove = ["as", "rendering"];
+ const attributes = sanitizeAttributes(props, remove);
+
+ return (
+
+ {rendering &&
+ rendering.map((resource) => {
+ const label = getLabelAsString(
+ resource.label,
+ attributes.lang,
+ ) as string;
+ return (
+
+
+ {label ? label : resource.id}
+
+
+ );
+ })}
+
+ );
+};
+
+export default Rendering;
diff --git a/src/components/Primitives/index.tsx b/src/components/Primitives/index.tsx
index 25c5803c3..937683231 100644
--- a/src/components/Primitives/index.tsx
+++ b/src/components/Primitives/index.tsx
@@ -5,6 +5,7 @@ import Markup from "src/components/Primitives/Markup/Markup";
import Metadata from "src/components/Primitives/Metadata/Metadata";
import MetadataItem from "src/components/Primitives/Metadata/Item";
import PartOf from "src/components/Primitives/PartOf/PartOf";
+import Rendering from "src/components/Primitives/Rendering/Rendering";
import RequiredStatement from "src/components/Primitives/RequiredStatement/RequiredStatement";
import SeeAlso from "src/components/Primitives/SeeAlso/SeeAlso";
import Summary from "src/components/Primitives/Summary/Summary";
@@ -18,6 +19,7 @@ import {
PrimitivesMetadata,
PrimitivesMetadataItem,
PrimitivesPartOf,
+ PrimitivesRendering,
PrimitivesRequiredStatement,
PrimitivesSeeAlso,
PrimitivesSummary,
@@ -33,6 +35,7 @@ export interface CloverPrimitivesComposition {
Metadata: React.FC;
MetadataItem: React.FC;
PartOf: React.FC;
+ Rendering: React.FC;
RequiredStatement: React.FC;
SeeAlso: React.FC;
Summary: React.FC;
@@ -52,6 +55,7 @@ Primitives.Markup = Markup;
Primitives.Metadata = Metadata;
Primitives.MetadataItem = MetadataItem;
Primitives.PartOf = PartOf;
+Primitives.Rendering = Rendering;
Primitives.RequiredStatement = RequiredStatement;
Primitives.SeeAlso = SeeAlso;
Primitives.Summary = Summary;
@@ -66,6 +70,7 @@ export {
Metadata,
MetadataItem,
PartOf,
+ Rendering,
RequiredStatement,
SeeAlso,
Summary,
diff --git a/src/components/Viewer/InformationPanel/About/About.tsx b/src/components/Viewer/InformationPanel/About/About.tsx
index 8eb179d27..0e96a969c 100644
--- a/src/components/Viewer/InformationPanel/About/About.tsx
+++ b/src/components/Viewer/InformationPanel/About/About.tsx
@@ -6,6 +6,7 @@ import {
Homepage,
Id,
Metadata,
+ Rendering,
RequiredStatement,
Rights,
SeeAlso,
@@ -29,6 +30,7 @@ const About: React.FC = () => {
const [homepage, setHomepage] = useState([]);
const [seeAlso, setSeeAlso] = useState([]);
+ const [rendering, setRendering] = useState([]);
const [thumbnail, setThumbnail] = useState([]);
useEffect(() => {
@@ -37,6 +39,7 @@ const About: React.FC = () => {
if (data.homepage?.length > 0) setHomepage(vault.get(data.homepage));
if (data.seeAlso?.length > 0) setSeeAlso(vault.get(data.seeAlso));
+ if (data.rendering?.length > 0) setRendering(vault.get(data.rendering));
if (data.thumbnail?.length > 0) setThumbnail(vault.get(data.thumbnail));
}, [activeManifest, vault]);
@@ -56,6 +59,9 @@ const About: React.FC = () => {
+
diff --git a/src/components/Viewer/Properties/Rendering.test.tsx b/src/components/Viewer/Properties/Rendering.test.tsx
new file mode 100644
index 000000000..efc766689
--- /dev/null
+++ b/src/components/Viewer/Properties/Rendering.test.tsx
@@ -0,0 +1,41 @@
+import { render, screen } from "@testing-library/react";
+import { PrimitivesExternalWebResource } from "src/types/primitives";
+import PropertiesRendering from "src/components/Viewer/Properties/Rendering";
+import React from "react";
+
+const json: PrimitivesExternalWebResource[] = [
+ {
+ id: "https://drive.google.com/file/d/1aWo1lORRVTQ0VveV3aP5Ym6hfVXUqr8_/view?usp=sharing",
+ type: "Text",
+ label: {
+ en: ['Download "A study guide: Josh MacPhee"'],
+ },
+ format: "application/pdf",
+ },
+ {
+ id: "https://fixtures.iiif.io/other/UCLA/kabuki_ezukushi_rtl.pdf",
+ type: "Text",
+ label: {
+ en: ["PDF version"],
+ },
+ format: "application/pdf",
+ },
+];
+
+describe("IIIF rendering property component", () => {
+ it("renders", () => {
+ render();
+
+ /**
+ * test anchors
+ */
+ const links = screen.getAllByRole("link");
+ links.forEach((element, index) => {
+ const text = json[index].label?.en?.join(", ") as string;
+ expect(element).toHaveTextContent(text);
+
+ const href = json[index].id as string;
+ expect(element.getAttribute("href")).toBe(href);
+ });
+ });
+});
diff --git a/src/components/Viewer/Properties/Rendering.tsx b/src/components/Viewer/Properties/Rendering.tsx
new file mode 100644
index 000000000..116e251f5
--- /dev/null
+++ b/src/components/Viewer/Properties/Rendering.tsx
@@ -0,0 +1,22 @@
+import { PrimitivesExternalWebResource } from "src/types/primitives";
+import { Rendering } from "src/components/Primitives";
+import React from "react";
+
+interface PropertiesRenderingProps {
+ rendering: PrimitivesExternalWebResource[];
+}
+
+const PropertiesRendering: React.FC = ({
+ rendering,
+}) => {
+ if (rendering?.length === 0) return <>>;
+
+ return (
+ <>
+ Alternate formats
+
+ >
+ );
+};
+
+export default PropertiesRendering;
diff --git a/src/components/Viewer/Properties/index.ts b/src/components/Viewer/Properties/index.ts
index 4cca36d98..2d960734b 100644
--- a/src/components/Viewer/Properties/index.ts
+++ b/src/components/Viewer/Properties/index.ts
@@ -1,6 +1,7 @@
import PropertiesHomepage from "src/components/Viewer/Properties/Homepage";
import PropertiesId from "src/components/Viewer/Properties/Id";
import PropertiesMetadata from "src/components/Viewer/Properties/Metadata";
+import PropertiesRendering from "src/components/Viewer/Properties/Rendering";
import PropertiesRequiredStatement from "src/components/Viewer/Properties/RequiredStatement";
import PropertiesRights from "src/components/Viewer/Properties/Rights";
import PropertiesSeeAlso from "src/components/Viewer/Properties/SeeAlso";
@@ -11,6 +12,7 @@ export {
PropertiesHomepage as Homepage,
PropertiesId as Id,
PropertiesMetadata as Metadata,
+ PropertiesRendering as Rendering,
PropertiesRequiredStatement as RequiredStatement,
PropertiesRights as Rights,
PropertiesSeeAlso as SeeAlso,
diff --git a/src/dev.tsx b/src/dev.tsx
index e342cc648..c73c4569f 100644
--- a/src/dev.tsx
+++ b/src/dev.tsx
@@ -7,6 +7,7 @@ import {
Label,
Metadata,
PartOf,
+ Rendering,
RequiredStatement,
SeeAlso,
Summary,
@@ -63,6 +64,9 @@ const App = () => {
+