@localess/react 3.0.0-dev.20260313085125 → 3.0.0-dev.20260313114825

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.
Files changed (2) hide show
  1. package/README.md +311 -54
  2. 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
- This client SDK is designed to work with the Localess API. It provides a simple way to interact with the Localess API from your React application.
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
- ### NPM
16
- ````bash
24
+ ```bash
25
+ # npm
17
26
  npm install @localess/react
18
- ````
19
27
 
20
- ### Yarn
21
- ````bash
28
+ # yarn
22
29
  yarn add @localess/react
23
- ````
24
30
 
25
- ## Usage
31
+ # pnpm
32
+ pnpm add @localess/react
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Getting Started
38
+
39
+ ### 1. Initialize the SDK
26
40
 
27
- ### Initialize and Visual Editor
28
- ````tsx
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: "I1LoVe2LocaLess4Rever",
35
- token: "Baz00KaT0KeN8S3CureLL",
36
- enableSync: true, //Enable Visual Editor Sync Script,
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
- ### React Component
47
- Example of Header Component with Menu Items
118
+ Marks a content block root element as editable.
48
119
 
49
- ````tsx
50
- import { llEditable, LocalessComponent } from "@localess/react";
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
- <nav {...llEditable(data)}>
55
- {data.items.map(item => <LocalessComponent key={item._id} data={item} />)}
56
- </nav>
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
- ### Listen for Visual Editor Events
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
- ````tsx
66
- import { llEditable, LocalessComponent, getLocalessClient } from "@localess/react";
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
- export default async function Page({searchParams}: {
69
- searchParams: Promise<{ [key: string]: string | string[] | undefined }>
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 {data} = await fetchData(locale?.toString());
73
- const [ pageData, setPageData ] = useState(data);
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 {...llEditable(data)}>
85
- {data.body.map(item => <LocalessComponent key={item._id} data={item} />)}
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
- async function fetchData(locale?: string): Promise<Content<Page>> {
91
- const client = getLocalessClient(); // Get LocalessClient Initlialized before
92
- return client.getContentBySlug<Page>('home', {locale: locale ? locale : undefined});
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.20260313085125",
3
+ "version": "3.0.0-dev.20260313114825",
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.20260313085125",
48
+ "@localess/client": "3.0.0-dev.20260313114825",
49
49
  "@tiptap/static-renderer": "^3.20.1",
50
50
  "@tiptap/html": "^3.20.1",
51
51
  "@tiptap/extension-bold": "^3.20.1",