Typed React components for rendering Crystallize content: Image, Video, Grid, and Content Transformer.
Use your favorite package manager:
# pnpm
pnpm add @crystallize/reactjs-components
# npm
npm install @crystallize/reactjs-components
# yarn
yarn add @crystallize/reactjs-components
All components are exported from the package root and ship with TypeScript types.
import { Image, Video, GridRenderer, GridRenderingType, ContentTransformer } from '@crystallize/reactjs-components';
Responsive wrapped in a
import { Image } from '@crystallize/reactjs-components';
const imageFromCrystallize = {
url: 'https://media.crystallize.com/.../image.jpg',
altText: 'A nice product',
variants: [
// ImageVariant[] from @crystallize/schema
{ url: '.../image@400.jpg', width: 400 },
{ url: '.../image@700.jpg', width: 700 },
{ url: '.../image@1000.webp', width: 1000 },
],
};
export function ProductImage() {
return <Image {...imageFromCrystallize} sizes="(max-width: 600px) 90vw, 700px" className="product-image" />;
}
Notes
- If the API returns caption in json/html/plainText, the component renders it as .
- You can provide width/height to avoid layout shift; otherwise the largest variant's dimensions are used when available.
Live demo: https://crystallizeapi.github.io/libraries/reactjs-components/image
Progressive video player that prefers HLS (m3u8) and falls back to MPEG-DASH (mpd). Renders a thumbnail and a play button, then hydrates the player.
Important
- This component is client-side only (it contains 'use client'). In Next.js, place it in a client component.
- Include the provided styles once in your app:
import '@crystallize/reactjs-components/assets/video/styles.css';
Usage
import { Video } from '@crystallize/reactjs-components';
const videoFromCrystallize = {
playlists: ['https://media.crystallize.com/.../master.m3u8', 'https://media.crystallize.com/.../stream.mpd'],
thumbnails: [
{
url: 'https://media.crystallize.com/.../thumb.jpg',
variants: [{ url: '.../thumb@700.jpg', width: 700 }],
},
],
};
export function HeroVideo() {
return (
<Video
{...videoFromCrystallize}
autoPlay
muted
controls
className="hero-video"
videoProps={{ playsInline: true }}
/>
);
}
Live demo: https://crystallizeapi.github.io/libraries/reactjs-components/video
Render Crystallize grids in React using CSS Grid (default), a semantic table, or row/col wrappers.
Fetch the grid via GraphQL (minimum shape shown):
query grid($id: Int!, $language: String!) {
grid(id: $id, language: $language) {
rows {
columns {
layout {
rowspan
colspan
colIndex
rowIndex
}
item {
name
}
}
}
}
}
Render
import { GridRenderer, GridRenderingType } from '@crystallize/reactjs-components';
const Cell = ({ cell }: { cell: any }) => <div>{cell.item?.name}</div>;
<>
{/* CSS Grid */}
<GridRenderer grid={grid} type={GridRenderingType.Div} cellComponent={Cell} />
{/* Table */}
<GridRenderer grid={grid} type={GridRenderingType.Table} cellComponent={Cell} />
{/* Row/Col wrappers */}
<GridRenderer grid={grid} type={GridRenderingType.RowCol} cellComponent={Cell} />
</>;
Customize per cell via a render-prop or styleForCell
<GridRenderer grid={grid} type={GridRenderingType.Div} cellComponent={Cell}>
{({ cells }) =>
cells.map((cell) => (
<div
key={`${cell.layout.rowIndex}-${cell.layout.colIndex}`}
style={{ gridColumn: `span ${cell.layout.colspan}`, gridRow: `span ${cell.layout.rowspan}` }}
>
{cell.item.name}
</div>
))
}
</GridRenderer>
Live demo: https://crystallizeapi.github.io/libraries/reactjs-components/grid
Render Crystallize rich text JSON to React elements with optional per-node overrides.
import { ContentTransformer, NodeContent, type Overrides, type NodeProps } from '@crystallize/reactjs-components';
const overrides: Overrides = {
link: (props: NodeProps) => (
<a href={props.metadata?.href} rel={props.metadata?.rel} target={props.metadata?.target}>
<NodeContent {...props} />
</a>
),
};
<ContentTransformer json={richTextJson} overrides={overrides} />;
Live demo: https://crystallizeapi.github.io/libraries/reactjs-components/content-transformer
- Image, Grid, and Content Transformer are SSR-friendly.
- Video must run on the client. In Next.js, put the usage in a Client Component.
MIT © Crystallize