@localess/react 3.0.0-dev.20260313084854 → 3.0.0-dev.20260313114528
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +311 -54
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,89 +6,346 @@
|
|
|
6
6
|
|
|
7
7
|
----
|
|
8
8
|
|
|
9
|
-
# Localess React
|
|
9
|
+
# Localess React SDK
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The `@localess/react` package is the official React integration for the [Localess](https://github.com/Lessify/localess) headless CMS platform. It provides component mapping, rich text rendering, and Visual Editor synchronization support for React applications.
|
|
12
|
+
|
|
13
|
+
> **⚠️ Security Notice:**
|
|
14
|
+
> This package uses `@localess/client` internally, which requires an API token for server-side data fetching.
|
|
15
|
+
> Always fetch Localess content server-side (e.g., Next.js Server Components, API routes, or `getServerSideProps`) and never expose your token in client-side code.
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js >= 20.0.0
|
|
20
|
+
- React 17, 18, or 19
|
|
12
21
|
|
|
13
22
|
## Installation
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
```bash
|
|
25
|
+
# npm
|
|
17
26
|
npm install @localess/react
|
|
18
|
-
````
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
````bash
|
|
28
|
+
# yarn
|
|
22
29
|
yarn add @localess/react
|
|
23
|
-
````
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
# pnpm
|
|
32
|
+
pnpm add @localess/react
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Getting Started
|
|
38
|
+
|
|
39
|
+
### 1. Initialize the SDK
|
|
26
40
|
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
Call `localessInit` once at application startup (e.g., in your root layout or `_app.tsx`) to configure the client, register your components, and optionally enable the Visual Editor.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
29
44
|
import { localessInit } from "@localess/react";
|
|
30
|
-
import { Page, Header, Teaser, Footer } from "@/components"
|
|
45
|
+
import { Page, Header, Teaser, Footer } from "@/components";
|
|
31
46
|
|
|
32
47
|
localessInit({
|
|
33
48
|
origin: "https://my-localess.web.app",
|
|
34
|
-
spaceId: "
|
|
35
|
-
token: "
|
|
36
|
-
enableSync: true, //Enable Visual Editor
|
|
49
|
+
spaceId: "YOUR_SPACE_ID",
|
|
50
|
+
token: "YOUR_API_TOKEN",
|
|
51
|
+
enableSync: true, // Enable Visual Editor sync script
|
|
37
52
|
components: {
|
|
38
53
|
'page': Page,
|
|
39
54
|
'header': Header,
|
|
40
55
|
'teaser': Teaser,
|
|
41
56
|
'footer': Footer,
|
|
42
57
|
},
|
|
43
|
-
})
|
|
44
|
-
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Initialization Options
|
|
62
|
+
|
|
63
|
+
| Option | Type | Required | Default | Description |
|
|
64
|
+
|--------|------|----------|---------|-------------|
|
|
65
|
+
| `origin` | `string` | ✅ | — | Fully qualified domain with protocol (e.g., `https://my-localess.web.app`) |
|
|
66
|
+
| `spaceId` | `string` | ✅ | — | Localess Space ID, found in Space settings |
|
|
67
|
+
| `token` | `string` | ✅ | — | Localess API token (keep secret — server-side only) |
|
|
68
|
+
| `version` | `'draft' \| string` | ❌ | `'published'` | Default content version |
|
|
69
|
+
| `debug` | `boolean` | ❌ | `false` | Enable debug logging |
|
|
70
|
+
| `cacheTTL` | `number \| false` | ❌ | `300000` | Cache TTL in milliseconds. Set `false` to disable |
|
|
71
|
+
| `components` | `Record<string, React.ElementType>` | ❌ | `{}` | Map of schema keys to React components |
|
|
72
|
+
| `fallbackComponent` | `React.ElementType` | ❌ | — | Component rendered when a schema key has no registered component |
|
|
73
|
+
| `enableSync` | `boolean` | ❌ | `false` | Load the Visual Editor sync script for live-editing support |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## `LocalessComponent`
|
|
78
|
+
|
|
79
|
+
`LocalessComponent` is a dynamic renderer that maps Localess content data to your registered React components by schema key. It automatically applies Visual Editor attributes when sync is enabled.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { LocalessComponent } from "@localess/react";
|
|
83
|
+
|
|
84
|
+
// Render a single content block
|
|
85
|
+
<LocalessComponent data={content.data} />
|
|
86
|
+
|
|
87
|
+
// Render a list of nested blocks
|
|
88
|
+
{data.body.map(item => (
|
|
89
|
+
<LocalessComponent
|
|
90
|
+
key={item._id}
|
|
91
|
+
data={item}
|
|
92
|
+
links={content.links}
|
|
93
|
+
references={content.references}
|
|
94
|
+
/>
|
|
95
|
+
))}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Props
|
|
99
|
+
|
|
100
|
+
| Prop | Type | Required | Description |
|
|
101
|
+
|------|------|----------|-------------|
|
|
102
|
+
| `data` | `ContentData` | ✅ | Content data object from Localess. The component looks up `data._schema` or `data.schema` in the component registry |
|
|
103
|
+
| `links` | `Links` | ❌ | Resolved content links map, forwarded to the rendered component |
|
|
104
|
+
| `references` | `References` | ❌ | Resolved references map, forwarded to the rendered component |
|
|
105
|
+
| `ref` | `React.Ref<HTMLElement>` | ❌ | Ref forwarded to the rendered component's root element |
|
|
106
|
+
| `...rest` | `any` | ❌ | Any additional props are forwarded to the rendered component |
|
|
107
|
+
|
|
108
|
+
> If a schema key is not registered and no `fallbackComponent` is configured, `LocalessComponent` renders an error message in the DOM.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Marking Editable Content
|
|
113
|
+
|
|
114
|
+
Use these helpers to add Visual Editor attributes to your JSX elements. They enable element highlighting and selection in the Localess Visual Editor.
|
|
115
|
+
|
|
116
|
+
### `localessEditable(content)`
|
|
45
117
|
|
|
46
|
-
|
|
47
|
-
Example of Header Component with Menu Items
|
|
118
|
+
Marks a content block root element as editable.
|
|
48
119
|
|
|
49
|
-
|
|
50
|
-
import {
|
|
120
|
+
```tsx
|
|
121
|
+
import { localessEditable } from "@localess/react";
|
|
122
|
+
|
|
123
|
+
const Header = ({ data }) => (
|
|
124
|
+
<nav {...localessEditable(data)}>
|
|
125
|
+
{/* ... */}
|
|
126
|
+
</nav>
|
|
127
|
+
);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `localessEditableField<T>(fieldName)`
|
|
131
|
+
|
|
132
|
+
Marks a specific field within a content block as editable, with type-safe field name inference when combined with generated types.
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { localessEditableField } from "@localess/react";
|
|
136
|
+
|
|
137
|
+
const Hero = ({ data }: { data: HeroBlock }) => (
|
|
138
|
+
<section {...localessEditable(data)}>
|
|
139
|
+
<h1 {...localessEditableField<HeroBlock>('title')}>{data.title}</h1>
|
|
140
|
+
<p {...localessEditableField<HeroBlock>('subtitle')}>{data.subtitle}</p>
|
|
141
|
+
</section>
|
|
142
|
+
);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
> **Deprecated:** `llEditable()` and `llEditableField()` are deprecated aliases. Use `localessEditable()` and `localessEditableField()` instead.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Rich Text Rendering
|
|
150
|
+
|
|
151
|
+
### `renderRichTextToReact(content)`
|
|
152
|
+
|
|
153
|
+
Converts a Localess `ContentRichText` object to a React node tree. Supports the full range of rich text formatting produced by the Localess editor.
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { renderRichTextToReact } from "@localess/react";
|
|
157
|
+
|
|
158
|
+
const Article = ({ data }) => (
|
|
159
|
+
<article>
|
|
160
|
+
<h1>{data.title}</h1>
|
|
161
|
+
<div>{renderRichTextToReact(data.body)}</div>
|
|
162
|
+
</article>
|
|
163
|
+
);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Supported rich text elements:**
|
|
167
|
+
|
|
168
|
+
- Document structure
|
|
169
|
+
- Headings (h1–h6)
|
|
170
|
+
- Paragraphs
|
|
171
|
+
- Text formatting: **bold**, *italic*, ~~strikethrough~~, underline
|
|
172
|
+
- Ordered and unordered lists
|
|
173
|
+
- Code blocks (with syntax highlighting support)
|
|
174
|
+
- Links (inline)
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Accessing the Client
|
|
179
|
+
|
|
180
|
+
### `getLocalessClient()`
|
|
181
|
+
|
|
182
|
+
Returns the `LocalessClient` instance created during `localessInit`. Use this in server-side data-fetching functions.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { getLocalessClient } from "@localess/react";
|
|
186
|
+
|
|
187
|
+
async function fetchPageData(locale?: string) {
|
|
188
|
+
const client = getLocalessClient();
|
|
189
|
+
return client.getContentBySlug<Page>('home', { locale });
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
> Throws an error if called before `localessInit` has been executed.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Component Registry API
|
|
198
|
+
|
|
199
|
+
These functions allow dynamic management of the component registry after initialization.
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import {
|
|
203
|
+
registerComponent,
|
|
204
|
+
unregisterComponent,
|
|
205
|
+
setComponents,
|
|
206
|
+
getComponent,
|
|
207
|
+
setFallbackComponent,
|
|
208
|
+
getFallbackComponent,
|
|
209
|
+
isSyncEnabled,
|
|
210
|
+
} from "@localess/react";
|
|
211
|
+
|
|
212
|
+
// Register a new component
|
|
213
|
+
registerComponent('hero-block', HeroBlock);
|
|
214
|
+
|
|
215
|
+
// Unregister a component
|
|
216
|
+
unregisterComponent('hero-block');
|
|
217
|
+
|
|
218
|
+
// Replace the entire registry
|
|
219
|
+
setComponents({ 'page': Page, 'hero': Hero });
|
|
220
|
+
|
|
221
|
+
// Retrieve a component by schema key
|
|
222
|
+
const Component = getComponent('hero');
|
|
223
|
+
|
|
224
|
+
// Configure the fallback component
|
|
225
|
+
setFallbackComponent(UnknownComponent);
|
|
226
|
+
|
|
227
|
+
// Get the current fallback component
|
|
228
|
+
const fallback = getFallbackComponent();
|
|
229
|
+
|
|
230
|
+
// Check if Visual Editor sync is enabled
|
|
231
|
+
const syncEnabled = isSyncEnabled();
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Assets
|
|
237
|
+
|
|
238
|
+
### `resolveAsset(asset)`
|
|
239
|
+
|
|
240
|
+
Resolves a `ContentAsset` object to a fully qualified URL using the initialized client's origin.
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
import { resolveAsset } from "@localess/react";
|
|
244
|
+
|
|
245
|
+
const Image = ({ data }) => (
|
|
246
|
+
<img src={resolveAsset(data.image)} alt={data.imageAlt} />
|
|
247
|
+
);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Visual Editor Events
|
|
253
|
+
|
|
254
|
+
When your application is opened inside the Localess Visual Editor, subscribe to live-editing events via `window.localess`.
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
'use client';
|
|
258
|
+
|
|
259
|
+
import { useEffect, useState } from "react";
|
|
260
|
+
import { getLocalessClient } from "@localess/react";
|
|
261
|
+
import type { Content } from "@localess/react";
|
|
262
|
+
|
|
263
|
+
export function PageClient({ initialData }: { initialData: Content<Page> }) {
|
|
264
|
+
const [pageData, setPageData] = useState(initialData.data);
|
|
265
|
+
|
|
266
|
+
useEffect(() => {
|
|
267
|
+
if (window.localess) {
|
|
268
|
+
window.localess.on(['input', 'change'], (event) => {
|
|
269
|
+
if (event.type === 'input' || event.type === 'change') {
|
|
270
|
+
setPageData(event.data);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}, []);
|
|
51
275
|
|
|
52
|
-
const Header = ({data}) => {
|
|
53
276
|
return (
|
|
54
|
-
<
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
277
|
+
<main {...localessEditable(pageData)}>
|
|
278
|
+
{pageData.body.map(item => (
|
|
279
|
+
<LocalessComponent key={item._id} data={item} />
|
|
280
|
+
))}
|
|
281
|
+
</main>
|
|
282
|
+
);
|
|
58
283
|
}
|
|
59
|
-
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
60
287
|
|
|
61
|
-
|
|
62
|
-
Your application can subscribe to the Localess Visual Editor Events.
|
|
63
|
-
Example from NextJS 15
|
|
288
|
+
## Full Example (Next.js 15 App Router)
|
|
64
289
|
|
|
65
|
-
|
|
66
|
-
|
|
290
|
+
```tsx
|
|
291
|
+
// app/layout.tsx (Server Component — safe to use API token here)
|
|
292
|
+
import { localessInit } from "@localess/react";
|
|
293
|
+
import { Page, Header, Teaser, Footer } from "@/components";
|
|
294
|
+
|
|
295
|
+
localessInit({
|
|
296
|
+
origin: process.env.LOCALESS_ORIGIN!,
|
|
297
|
+
spaceId: process.env.LOCALESS_SPACE_ID!,
|
|
298
|
+
token: process.env.LOCALESS_TOKEN!,
|
|
299
|
+
enableSync: process.env.NODE_ENV === 'development',
|
|
300
|
+
components: { Page, Header, Teaser, Footer },
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
export default function RootLayout({ children }) {
|
|
304
|
+
return <html><body>{children}</body></html>;
|
|
305
|
+
}
|
|
306
|
+
```
|
|
67
307
|
|
|
68
|
-
|
|
69
|
-
|
|
308
|
+
```tsx
|
|
309
|
+
// app/page.tsx (Server Component)
|
|
310
|
+
import { getLocalessClient, LocalessComponent, localessEditable } from "@localess/react";
|
|
311
|
+
import type { Page } from "./.localess/localess";
|
|
312
|
+
|
|
313
|
+
export default async function HomePage({
|
|
314
|
+
searchParams,
|
|
315
|
+
}: {
|
|
316
|
+
searchParams: Promise<{ locale?: string }>;
|
|
70
317
|
}) {
|
|
71
|
-
const {locale} = await searchParams
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (window.localess) {
|
|
77
|
-
window.localess.on(['input', 'change'], (event) => {
|
|
78
|
-
if (event.type === 'input' || event.type === 'change') {
|
|
79
|
-
setPageData(event.data);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
318
|
+
const { locale } = await searchParams;
|
|
319
|
+
const client = getLocalessClient();
|
|
320
|
+
const content = await client.getContentBySlug<Page>('home', { locale });
|
|
321
|
+
|
|
83
322
|
return (
|
|
84
|
-
<main {...
|
|
85
|
-
{data
|
|
323
|
+
<main {...localessEditable(content.data)}>
|
|
324
|
+
{content.data?.body.map(item => (
|
|
325
|
+
<LocalessComponent
|
|
326
|
+
key={item._id}
|
|
327
|
+
data={item}
|
|
328
|
+
links={content.links}
|
|
329
|
+
references={content.references}
|
|
330
|
+
/>
|
|
331
|
+
))}
|
|
86
332
|
</main>
|
|
87
|
-
)
|
|
333
|
+
);
|
|
88
334
|
}
|
|
335
|
+
```
|
|
89
336
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Re-exported from `@localess/client`
|
|
340
|
+
|
|
341
|
+
The following are re-exported for convenience so you only need to import from `@localess/react`:
|
|
342
|
+
|
|
343
|
+
**Types:** `Content`, `ContentData`, `ContentMetadata`, `ContentDataSchema`, `ContentDataField`, `ContentAsset`, `ContentRichText`, `ContentLink`, `ContentReference`, `Links`, `References`, `Translations`, `LocalessClient`, `LocalessSync`, `EventToApp`, `EventCallback`, `EventToAppType`
|
|
344
|
+
|
|
345
|
+
**Functions:** `localessEditable`, `localessEditableField`, `llEditable` *(deprecated)*, `llEditableField` *(deprecated)*, `isBrowser`, `isServer`, `isIframe`
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## License
|
|
350
|
+
|
|
351
|
+
[MIT](../../LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localess/react",
|
|
3
|
-
"version": "3.0.0-dev.
|
|
3
|
+
"version": "3.0.0-dev.20260313114528",
|
|
4
4
|
"description": "ReactJS JavaScript/TypeScript SDK for Localess's API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"localess",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"react-dom": "^17 || ^18 || ^19"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@localess/client": "3.0.0-dev.
|
|
48
|
+
"@localess/client": "3.0.0-dev.20260313114528",
|
|
49
49
|
"@tiptap/static-renderer": "^3.20.1",
|
|
50
50
|
"@tiptap/html": "^3.20.1",
|
|
51
51
|
"@tiptap/extension-bold": "^3.20.1",
|