Skip to content

Commit 3f1cea4

Browse files
committed
feat(v5): clean unused librairies and move everything to Core Next
1 parent 694ed92 commit 3f1cea4

File tree

11 files changed

+262
-128
lines changed

11 files changed

+262
-128
lines changed

.github/workflows/release.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- name: ⬇️ Checkout repo
14-
uses: actions/checkout@v3
14+
uses: actions/checkout@v4
1515

1616
- name: ⎔ Setup node
17-
uses: actions/setup-node@v3
17+
uses: actions/setup-node@v4
1818
with:
19-
node-version: 18
19+
node-version: 22
2020
always-auth: true
2121
registry-url: 'https://registry.npmjs.org'
2222
scope: '@crystallize'

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License
22

3-
Copyright (c) 2022 Crystallize, https://crystallize.com
3+
Copyright (c) 2025 Crystallize, https://crystallize.com
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 116 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,107 @@
1-
# React JS Components
1+
# Crystallize React components
22

3-
This brings Image, Grid and Content Transformer component to ease your rendering when using React JS.
3+
Typed React components for rendering Crystallize content: Image, Video, Grid, and Content Transformer.
44

55
## Installation
66

7-
With NPM:
7+
Use your favorite package manager:
88

99
```bash
10+
# pnpm
11+
pnpm add @crystallize/reactjs-components
12+
13+
# npm
1014
npm install @crystallize/reactjs-components
15+
16+
# yarn
17+
yarn add @crystallize/reactjs-components
1118
```
1219

13-
With Yarn:
20+
All components are exported from the package root and ship with TypeScript types.
1421

15-
```bash
16-
yarn add @crystallize/reactjs-components
22+
```typescript
23+
import { Image, Video, GridRenderer, GridRenderingType, ContentTransformer } from '@crystallize/reactjs-components';
1724
```
1825

1926
## Image
2027

21-
This output an `img` tag with different source variations from Crystallize using _srcset_. Use this to easily build responsive images powered by Crystallize.
28+
Responsive <img> wrapped in a <picture> with srcset support. Pass the image object you get from Crystallize.
29+
30+
```typescript
31+
import { Image } from '@crystallize/reactjs-components';
2232

23-
```javascript
24-
import { Image } from '@crystallize/reactjs-components/dist/image';
2533
const imageFromCrystallize = {
26-
url: '...',
27-
variants: [...]
28-
}
34+
url: 'https://media.crystallize.com/.../image.jpg',
35+
altText: 'A nice product',
36+
variants: [
37+
// ImageVariant[] from @crystallize/schema
38+
{ url: '.../[email protected]', width: 400 },
39+
{ url: '.../[email protected]', width: 700 },
40+
{ url: '.../[email protected]', width: 1000 },
41+
],
42+
};
2943

30-
<Image
31-
{...imageFromCrystallize}
32-
sizes="(max-width: 400px) 300w, 700px"
33-
/>
44+
export function ProductImage() {
45+
return <Image {...imageFromCrystallize} sizes="(max-width: 600px) 90vw, 700px" className="product-image" />;
46+
}
3447
```
3548

36-
There is a live demo: https://crystallizeapi.github.io/libraries/reactjs-components/image
49+
Notes
50+
51+
- If the API returns caption in json/html/plainText, the component renders it as <figcaption>.
52+
- You can provide width/height to avoid layout shift; otherwise the largest variant's dimensions are used when available.
53+
54+
Live demo: https://crystallizeapi.github.io/libraries/reactjs-components/image
3755

3856
## Video
3957

40-
This output videos from Crystallize using the native video element.
58+
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.
59+
60+
Important
4161

42-
```javascript
43-
import { Video } from '@crystallize/reactjs-components/dist/video';
62+
- This component is client-side only (it contains 'use client'). In Next.js, place it in a client component.
63+
- Include the provided styles once in your app:
64+
65+
```typescript
4466
import '@crystallize/reactjs-components/assets/video/styles.css';
67+
```
68+
69+
Usage
70+
71+
```typescript
72+
import { Video } from '@crystallize/reactjs-components';
73+
4574
const videoFromCrystallize = {
46-
playlists: [...],
47-
thumbnails: [...]
48-
}
75+
playlists: ['https://media.crystallize.com/.../master.m3u8', 'https://media.crystallize.com/.../stream.mpd'],
76+
thumbnails: [
77+
{
78+
url: 'https://media.crystallize.com/.../thumb.jpg',
79+
variants: [{ url: '.../[email protected]', width: 700 }],
80+
},
81+
],
82+
};
4983

50-
<Video
51-
{...videoFromCrystallize}
52-
thumbnmailProps={{ sizes: "(max-width: 700px) 90vw, 700px" }}
53-
/>
84+
export function HeroVideo() {
85+
return (
86+
<Video
87+
{...videoFromCrystallize}
88+
autoPlay
89+
muted
90+
controls
91+
className="hero-video"
92+
videoProps={{ playsInline: true }}
93+
/>
94+
);
95+
}
5496
```
5597

56-
There is a live demo: https://crystallizeapi.github.io/libraries/reactjs-components/video
98+
Live demo: https://crystallizeapi.github.io/libraries/reactjs-components/video
5799

58100
## Grid
59101

60-
That makes it easy to render Crystallize grids with React JS. In order to use the grid renderer you'll need to have fetched your grid model. This can be fetched fairly easily from Crystallize's API via GraphQL.
102+
Render Crystallize grids in React using CSS Grid (default), a semantic table, or row/col wrappers.
61103

62-
At the minimum you will need to fetch layout of each column and some properties on the item. Your query might look something like this:
104+
Fetch the grid via GraphQL (minimum shape shown):
63105

64106
```graphql
65107
query grid($id: Int!, $language: String!) {
@@ -81,49 +123,54 @@ query grid($id: Int!, $language: String!) {
81123
}
82124
```
83125

84-
Then, inside your component, render the Grid, passing through the grid model as a prop. By default, the grid is rendered using CSS grid but it could also be a Table.
126+
Render
85127

86-
```javascript
87-
<GridRenderer grid={grid} type={GridRenderingType.Div} cellComponent={Cell} />
88-
<GridRenderer grid={grid} type={GridRenderingType.Table} cellComponent={Cell} />
89-
<GridRenderer grid={grid} type={GridRenderingType.RowCol} cellComponent={Cell} />
90-
```
128+
```typescript
129+
import { GridRenderer, GridRenderingType } from '@crystallize/reactjs-components';
91130

92-
There is a live demo: https://crystallizeapi.github.io/libraries/reactjs-components/grid
131+
const Cell = ({ cell }: { cell: any }) => <div>{cell.item?.name}</div>;
93132

94-
### To go further
133+
<>
134+
{/* CSS Grid */}
135+
<GridRenderer grid={grid} type={GridRenderingType.Div} cellComponent={Cell} />
95136

96-
If you want full control over each of the cells, you can instead supply a function as the children of the grid component. This will allow you to iterate over each of the cells and mutate them as you please.
137+
{/* Table */}
138+
<GridRenderer grid={grid} type={GridRenderingType.Table} cellComponent={Cell} />
97139

98-
```javascript
99-
const children = ({ cells }) => {
100-
return cells.map((cell) => (
101-
<div
102-
style={{
103-
gridColumn: `span ${cell.layout.colspan}`,
104-
gridRow: `span ${cell.layout.rowspan}`,
105-
}}
106-
>
107-
{cell.item.name}
108-
</div>
109-
));
110-
};
140+
{/* Row/Col wrappers */}
141+
<GridRenderer grid={grid} type={GridRenderingType.RowCol} cellComponent={Cell} />
142+
</>;
143+
```
111144

112-
return (
113-
<GridRenderer grid={grid} type={GridRenderingType.Div} cellComponent={Cell}>
114-
{children}
115-
</GridRenderer>
116-
);
145+
Customize per cell via a render-prop or styleForCell
146+
147+
```tsx
148+
<GridRenderer grid={grid} type={GridRenderingType.Div} cellComponent={Cell}>
149+
{({ cells }) =>
150+
cells.map((cell) => (
151+
<div
152+
key={`${cell.layout.rowIndex}-${cell.layout.colIndex}`}
153+
style={{ gridColumn: `span ${cell.layout.colspan}`, gridRow: `span ${cell.layout.rowspan}` }}
154+
>
155+
{cell.item.name}
156+
</div>
157+
))
158+
}
159+
</GridRenderer>
117160
```
118161

162+
Live demo: https://crystallizeapi.github.io/libraries/reactjs-components/grid
163+
119164
## Content Transformer
120165

121-
This helps you to transform Crystallize rich text json to React html components.
166+
Render Crystallize rich text JSON to React elements with optional per-node overrides.
167+
168+
```tsx
169+
import { ContentTransformer, NodeContent, type Overrides, type NodeProps } from '@crystallize/reactjs-components';
122170

123-
```javascript
124171
const overrides: Overrides = {
125172
link: (props: NodeProps) => (
126-
<a href={props.metadata?.href}>
173+
<a href={props.metadata?.href} rel={props.metadata?.rel} target={props.metadata?.target}>
127174
<NodeContent {...props} />
128175
</a>
129176
),
@@ -132,6 +179,13 @@ const overrides: Overrides = {
132179
<ContentTransformer json={richTextJson} overrides={overrides} />;
133180
```
134181

135-
There is a live demo: https://crystallizeapi.github.io/libraries/reactjs-components/content-transformer
182+
Live demo: https://crystallizeapi.github.io/libraries/reactjs-components/content-transformer
183+
184+
## Notes on SSR
185+
186+
- Image, Grid, and Content Transformer are SSR-friendly.
187+
- Video must run on the client. In Next.js, put the usage in a Client Component.
188+
189+
## License
136190

137-
[crystallizeobject]: crystallize_marketing|folder|6269c9819161f671155d939d
191+
MIT © Crystallize

package.json

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@crystallize/reactjs-components",
33
"license": "MIT",
4-
"version": "3.0.1",
4+
"version": "5.0.0",
55
"type": "module",
66
"author": "Crystallize <[email protected]> (https://crystallize.com)",
77
"contributors": [
@@ -21,9 +21,9 @@
2121
},
2222
"exports": {
2323
".": {
24+
"types": "./dist/index.d.ts",
2425
"import": "./dist/index.js",
25-
"require": "./dist/index.cjs",
26-
"types": "./dist/index.d.ts"
26+
"require": "./dist/index.cjs"
2727
},
2828
"./assets/video/styles.css": "./assets/video/styles.css"
2929
},
@@ -35,15 +35,16 @@
3535
"react-dom": "*"
3636
},
3737
"dependencies": {
38-
"@crystallize/js-api-client": "workspace:*"
38+
"@crystallize/schema": "workspace:*"
3939
},
4040
"devDependencies": {
41-
"@tsconfig/node20": "^20.1.4",
42-
"@types/node": "^20.14.9",
43-
"@types/react": "^17 || ^18",
44-
"@types/react-dom": "^17 || ^18",
45-
"tsup": "^8.1.0",
46-
"vitest": "^1.6.0",
47-
"typescript": "^5.5.2"
41+
"@crystallize/js-api-client": "workspace:*",
42+
"@tsconfig/node22": "^22.0.2",
43+
"@types/node": "^24.2.0",
44+
"@types/react": "^19.1.9",
45+
"@types/react-dom": "^19.1.7",
46+
"tsup": "^8.5.0",
47+
"typescript": "^5.9.2",
48+
"vitest": "^3.2.4"
4849
}
4950
}

src/grid/GridRenderer.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ import { CSSGrid } from './CSSGrid.js';
33
import { getGridDimensions } from './grid-renderer-utils.js';
44
import { RowCol } from './RowCol.js';
55
import { Table } from './Table.js';
6-
import { GridRow, GridCell, GridRendererProps, GridRenderingType } from './types.js';
6+
import {
7+
GridRow,
8+
GridCell,
9+
GridRendererProps,
10+
GridRenderingType,
11+
TableGridProps,
12+
RowColGridProps,
13+
CSSGridProps,
14+
} from './types.js';
715

816
export const GridRenderer: FunctionComponent<GridRendererProps> = ({
917
cellComponent,
@@ -14,13 +22,14 @@ export const GridRenderer: FunctionComponent<GridRendererProps> = ({
1422
...props
1523
}) => {
1624
if (!cellComponent && !children) {
17-
console.error('@crystallize/grid-renderer: missing ´cellComponent` or children function');
25+
console.error(`@crystallize/grid-renderer: missing 'cellComponent' or children function`);
1826
return null;
1927
}
2028
if (!grid.rows.length) return null;
2129
const dimensions = getGridDimensions(grid.rows);
2230

2331
if (type === GridRenderingType.Table) {
32+
const tableChildren = children as TableGridProps['children'];
2433
return (
2534
<Table
2635
cellComponent={cellComponent}
@@ -29,11 +38,12 @@ export const GridRenderer: FunctionComponent<GridRendererProps> = ({
2938
styleForCell={styleForCell}
3039
{...props}
3140
>
32-
{children}
41+
{tableChildren}
3342
</Table>
3443
);
3544
}
3645
if (type === GridRenderingType.RowCol) {
46+
const rowColChildren = children as RowColGridProps['children'];
3747
return (
3848
<RowCol
3949
cellComponent={cellComponent}
@@ -42,13 +52,14 @@ export const GridRenderer: FunctionComponent<GridRendererProps> = ({
4252
styleForCell={styleForCell}
4353
{...props}
4454
>
45-
{children}
55+
{rowColChildren}
4656
</RowCol>
4757
);
4858
}
4959

5060
const cells = grid.rows.reduce<GridCell[]>((memo, row) => memo.concat(row.columns), []);
5161

62+
const cssGridChildren = children as CSSGridProps['children'];
5263
return (
5364
<CSSGrid
5465
cellComponent={cellComponent}
@@ -57,7 +68,7 @@ export const GridRenderer: FunctionComponent<GridRendererProps> = ({
5768
styleForCell={styleForCell}
5869
{...props}
5970
>
60-
{children}
71+
{cssGridChildren}
6172
</CSSGrid>
6273
);
6374
};

0 commit comments

Comments
 (0)