@localess/react 3.0.1-dev.20260408170501 → 3.0.1-dev.20260408183448
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 +263 -27
- package/SKILL.md +195 -24
- package/dist/index.d.mts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +33 -12
- package/dist/index.mjs +27 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -249,46 +249,186 @@ const Image = ({ data }) => (
|
|
|
249
249
|
|
|
250
250
|
---
|
|
251
251
|
|
|
252
|
+
## `useLocaless` Hook
|
|
253
|
+
|
|
254
|
+
`useLocaless<T>` fetches content by slug in a Client Component and automatically subscribes to Visual Editor live updates when `enableSync` is active.
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
'use client';
|
|
258
|
+
|
|
259
|
+
import { useLocaless, LocalessComponent } from "@localess/react";
|
|
260
|
+
import type { Page } from "./.localess/localess";
|
|
261
|
+
|
|
262
|
+
export function PageView({ slug }: { slug: string }) {
|
|
263
|
+
const content = useLocaless<Page>(slug, { locale: 'en' });
|
|
264
|
+
|
|
265
|
+
if (!content) return <div>Loading…</div>;
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<main>
|
|
269
|
+
{content.data.body.map(item => (
|
|
270
|
+
<LocalessComponent key={item._id} data={item} links={content.links} />
|
|
271
|
+
))}
|
|
272
|
+
</main>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Parameters
|
|
278
|
+
|
|
279
|
+
| Parameter | Type | Required | Description |
|
|
280
|
+
|-----------|------|----------|-------------|
|
|
281
|
+
| `slug` | `string \| string[]` | ✅ | Content slug. Arrays are joined with `/` — e.g. `['blog', 'post']` → `'blog/post'` |
|
|
282
|
+
| `options` | `ContentFetchParams` | ❌ | Same fetch options as `getContentBySlug` (locale, version, resolveReference, resolveLink) |
|
|
283
|
+
|
|
284
|
+
Returns `Content<T> | undefined` — `undefined` while the initial fetch is in progress.
|
|
285
|
+
|
|
286
|
+
When `enableSync` is active and the page is rendered inside the Localess Visual Editor iframe, the hook automatically subscribes to `input` / `change` events and updates the returned content in place.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Link Utilities
|
|
291
|
+
|
|
292
|
+
### `findLink(links, link)`
|
|
293
|
+
|
|
294
|
+
Resolves a `ContentLink` field to a URL string. Use it to build `href` values from Localess content links.
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import { findLink } from "@localess/react";
|
|
298
|
+
|
|
299
|
+
// type: 'content' → '/' + fullSlug, or '/not-found' if not in map
|
|
300
|
+
// type: 'url' → raw URI unchanged
|
|
301
|
+
const href = findLink(content.links, data.ctaLink);
|
|
302
|
+
|
|
303
|
+
const NavLink = ({ data, links }) => (
|
|
304
|
+
<a href={findLink(links, data.link)}>{data.label}</a>
|
|
305
|
+
);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
252
310
|
## Visual Editor Events
|
|
253
311
|
|
|
254
|
-
|
|
312
|
+
### With `useLocaless` Hook
|
|
313
|
+
|
|
314
|
+
When `enableSync: true` is set in `localessInit`, the `useLocaless` hook handles the full cycle automatically — initial fetch and live sync updates — with no extra wiring needed.
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
'use client';
|
|
318
|
+
|
|
319
|
+
import { useLocaless, LocalessComponent, localessEditable } from "@localess/react";
|
|
320
|
+
import type { Page } from "./.localess/localess";
|
|
321
|
+
|
|
322
|
+
export function PageView({ slug, locale }: { slug: string; locale?: string }) {
|
|
323
|
+
const content = useLocaless<Page>(slug, { locale });
|
|
324
|
+
|
|
325
|
+
if (!content) return null;
|
|
326
|
+
|
|
327
|
+
return (
|
|
328
|
+
<main {...localessEditable(content.data)}>
|
|
329
|
+
{content.data?.body.map(item => (
|
|
330
|
+
<LocalessComponent key={item._id} data={item} links={content.links} references={content.references} />
|
|
331
|
+
))}
|
|
332
|
+
</main>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### With `LocalessDocument` Component
|
|
338
|
+
|
|
339
|
+
`LocalessDocument` is a component alternative to the hook. Pass it server-fetched content data and it handles live sync updates internally, delegating rendering to `LocalessComponent`.
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
// app/[locale]/page.tsx (Server Component — fetches data)
|
|
343
|
+
import { getLocalessClient, LocalessDocument } from "@localess/react";
|
|
344
|
+
import type { Page } from "./.localess/localess";
|
|
345
|
+
|
|
346
|
+
export default async function HomePage({ params }: { params: Promise<{ locale?: string }> }) {
|
|
347
|
+
const { locale } = await params;
|
|
348
|
+
const client = getLocalessClient();
|
|
349
|
+
const content = await client.getContentBySlug<Page>('home', { locale });
|
|
350
|
+
|
|
351
|
+
return (
|
|
352
|
+
<LocalessDocument
|
|
353
|
+
data={content.data}
|
|
354
|
+
links={content.links}
|
|
355
|
+
references={content.references}
|
|
356
|
+
/>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Props:**
|
|
362
|
+
|
|
363
|
+
| Prop | Type | Required | Description |
|
|
364
|
+
|------|------|----------|-------------|
|
|
365
|
+
| `data` | `ContentData` | ✅ | Initial content data (typically server-fetched) |
|
|
366
|
+
| `links` | `Links` | ❌ | Resolved links map, forwarded to the inner `LocalessComponent` |
|
|
367
|
+
| `references` | `References` | ❌ | Resolved references map, forwarded to the inner `LocalessComponent` |
|
|
368
|
+
| `ref` | `React.Ref<HTMLElement>` | ❌ | Forwarded to the rendered root element |
|
|
369
|
+
| `...rest` | `any` | ❌ | Any additional props are forwarded |
|
|
370
|
+
|
|
371
|
+
> `LocalessDocument` subscribes to `input` / `change` editor events automatically when `enableSync` is active. It is a Client Component internally — no `'use client'` directive needed at the call site in Server Components.
|
|
372
|
+
|
|
373
|
+
### Manual Integration
|
|
374
|
+
|
|
375
|
+
If you manage content state yourself without `useLocaless` or `LocalessDocument`, subscribe to editor events directly via `window.localess`:
|
|
255
376
|
|
|
256
377
|
```tsx
|
|
257
378
|
'use client';
|
|
258
379
|
|
|
259
380
|
import { useEffect, useState } from "react";
|
|
260
|
-
import {
|
|
261
|
-
import type { Content } from "
|
|
381
|
+
import { LocalessComponent, localessEditable, isSyncEnabled, isBrowser } from "@localess/react";
|
|
382
|
+
import type { Content, Page } from "./.localess/localess";
|
|
262
383
|
|
|
263
|
-
export function PageClient({
|
|
264
|
-
const [pageData, setPageData] = useState(
|
|
384
|
+
export function PageClient({ initialContent }: { initialContent: Content<Page> }) {
|
|
385
|
+
const [pageData, setPageData] = useState(initialContent.data);
|
|
265
386
|
|
|
266
387
|
useEffect(() => {
|
|
267
|
-
if (window.localess) {
|
|
388
|
+
if (isSyncEnabled() && isBrowser() && window.localess) {
|
|
268
389
|
window.localess.on(['input', 'change'], (event) => {
|
|
269
390
|
if (event.type === 'input' || event.type === 'change') {
|
|
270
391
|
setPageData(event.data);
|
|
271
392
|
}
|
|
272
393
|
});
|
|
273
394
|
}
|
|
395
|
+
// No cleanup needed: window.localess has no .off() method
|
|
274
396
|
}, []);
|
|
275
397
|
|
|
276
398
|
return (
|
|
277
399
|
<main {...localessEditable(pageData)}>
|
|
278
|
-
{pageData
|
|
279
|
-
<LocalessComponent key={item._id} data={item} />
|
|
400
|
+
{pageData?.body.map(item => (
|
|
401
|
+
<LocalessComponent key={item._id} data={item} links={initialContent.links} references={initialContent.references} />
|
|
280
402
|
))}
|
|
281
403
|
</main>
|
|
282
404
|
);
|
|
283
405
|
}
|
|
284
406
|
```
|
|
285
407
|
|
|
408
|
+
**Available events via `window.localess.on()`:**
|
|
409
|
+
|
|
410
|
+
| Event | When |
|
|
411
|
+
|-------|------|
|
|
412
|
+
| `input` | User is typing in a field (real-time preview) |
|
|
413
|
+
| `change` | Field value confirmed |
|
|
414
|
+
| `save` | Content saved |
|
|
415
|
+
| `publish` | Content published |
|
|
416
|
+
| `pong` | Editor heartbeat response |
|
|
417
|
+
| `enterSchema` | Editor cursor enters a schema block |
|
|
418
|
+
| `hoverSchema` | Editor cursor hovers over a schema block |
|
|
419
|
+
|
|
420
|
+
> `window.localess` only exposes `.on()` and `.onChange()` — there is no `.off()` method.
|
|
421
|
+
|
|
286
422
|
---
|
|
287
423
|
|
|
288
|
-
## Full Example (Next.js
|
|
424
|
+
## Full Example (Next.js 16.2 App Router)
|
|
425
|
+
|
|
426
|
+
The recommended Next.js pattern is to **preload data server-side** and pass it to the Client Component. This avoids a loading flash — the page renders immediately with server data, then Visual Editor sync kicks in if active.
|
|
427
|
+
|
|
428
|
+
### Setup — `app/layout.tsx`
|
|
289
429
|
|
|
290
430
|
```tsx
|
|
291
|
-
//
|
|
431
|
+
// Server Component — safe to use API token here
|
|
292
432
|
import { localessInit } from "@localess/react";
|
|
293
433
|
import { Page, Header, Teaser, Footer } from "@/components";
|
|
294
434
|
|
|
@@ -296,38 +436,132 @@ localessInit({
|
|
|
296
436
|
origin: process.env.LOCALESS_ORIGIN!,
|
|
297
437
|
spaceId: process.env.LOCALESS_SPACE_ID!,
|
|
298
438
|
token: process.env.LOCALESS_TOKEN!,
|
|
299
|
-
enableSync: process.env.NODE_ENV
|
|
439
|
+
enableSync: process.env.NODE_ENV !== 'production',
|
|
300
440
|
components: { Page, Header, Teaser, Footer },
|
|
301
441
|
});
|
|
302
442
|
|
|
303
|
-
export default function RootLayout({ children }) {
|
|
443
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
304
444
|
return <html><body>{children}</body></html>;
|
|
305
445
|
}
|
|
306
446
|
```
|
|
307
447
|
|
|
448
|
+
### Server Component — `app/[locale]/page.tsx`
|
|
449
|
+
|
|
450
|
+
Fetches content during SSR and passes it as a prop. The client component receives it already populated — no loading state needed.
|
|
451
|
+
|
|
308
452
|
```tsx
|
|
309
|
-
|
|
310
|
-
import {
|
|
311
|
-
|
|
453
|
+
import { getLocalessClient } from "@localess/react";
|
|
454
|
+
import type { Content, Page } from "./.localess/localess";
|
|
455
|
+
|
|
456
|
+
// Choose one of the three client components below
|
|
457
|
+
import { PageClientHook } from "./page-client-hook";
|
|
312
458
|
|
|
313
459
|
export default async function HomePage({
|
|
314
|
-
|
|
460
|
+
params,
|
|
315
461
|
}: {
|
|
316
|
-
|
|
462
|
+
params: Promise<{ locale?: string }>;
|
|
317
463
|
}) {
|
|
318
|
-
const { locale } = await
|
|
319
|
-
const
|
|
320
|
-
|
|
464
|
+
const { locale } = await params;
|
|
465
|
+
const content = await getLocalessClient().getContentBySlug<Page>('home', { locale });
|
|
466
|
+
|
|
467
|
+
return <PageClientHook initialContent={content} locale={locale} />;
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Client Component — Option A: `useLocaless` Hook
|
|
472
|
+
|
|
473
|
+
The hook re-fetches on the client and falls back to the server-preloaded data until it resolves. Live sync is wired automatically.
|
|
474
|
+
|
|
475
|
+
```tsx
|
|
476
|
+
// app/[locale]/page-client-hook.tsx
|
|
477
|
+
'use client';
|
|
478
|
+
|
|
479
|
+
import { useLocaless, LocalessComponent, localessEditable } from "@localess/react";
|
|
480
|
+
import type { Content, Page } from "./.localess/localess";
|
|
481
|
+
|
|
482
|
+
export function PageClientHook({
|
|
483
|
+
initialContent,
|
|
484
|
+
locale,
|
|
485
|
+
}: {
|
|
486
|
+
initialContent: Content<Page>;
|
|
487
|
+
locale?: string;
|
|
488
|
+
}) {
|
|
489
|
+
// ?? initialContent: renders with server data immediately, switches to hook result once ready
|
|
490
|
+
const content = useLocaless<Page>('home', { locale }) ?? initialContent;
|
|
321
491
|
|
|
322
492
|
return (
|
|
323
493
|
<main {...localessEditable(content.data)}>
|
|
324
494
|
{content.data?.body.map(item => (
|
|
325
|
-
<LocalessComponent
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
495
|
+
<LocalessComponent key={item._id} data={item} links={content.links} references={content.references} />
|
|
496
|
+
))}
|
|
497
|
+
</main>
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Client Component — Option B: `LocalessDocument` Component
|
|
503
|
+
|
|
504
|
+
Skips client re-fetch entirely — uses server-preloaded data and only subscribes to live sync events. Simpler when you don't need client-side refetching.
|
|
505
|
+
|
|
506
|
+
```tsx
|
|
507
|
+
// app/[locale]/page.tsx (Server Component — no separate client file needed)
|
|
508
|
+
import { getLocalessClient, LocalessDocument } from "@localess/react";
|
|
509
|
+
import type { Page } from "./.localess/localess";
|
|
510
|
+
|
|
511
|
+
export default async function HomePage({
|
|
512
|
+
params,
|
|
513
|
+
}: {
|
|
514
|
+
params: Promise<{ locale?: string }>;
|
|
515
|
+
}) {
|
|
516
|
+
const { locale } = await params;
|
|
517
|
+
const content = await getLocalessClient().getContentBySlug<Page>('home', { locale });
|
|
518
|
+
|
|
519
|
+
// LocalessDocument handles sync internally — no 'use client' wrapper needed here
|
|
520
|
+
return (
|
|
521
|
+
<LocalessDocument
|
|
522
|
+
data={content.data}
|
|
523
|
+
links={content.links}
|
|
524
|
+
references={content.references}
|
|
525
|
+
/>
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Client Component — Option C: Manual
|
|
531
|
+
|
|
532
|
+
Full control over state and sync subscription. Use when you need custom logic around live updates.
|
|
533
|
+
|
|
534
|
+
```tsx
|
|
535
|
+
// app/[locale]/page-client-manual.tsx
|
|
536
|
+
'use client';
|
|
537
|
+
|
|
538
|
+
import { useEffect, useState } from "react";
|
|
539
|
+
import { LocalessComponent, localessEditable, isSyncEnabled, isBrowser } from "@localess/react";
|
|
540
|
+
import type { Content, Page } from "./.localess/localess";
|
|
541
|
+
|
|
542
|
+
export function PageClientManual({
|
|
543
|
+
initialContent,
|
|
544
|
+
}: {
|
|
545
|
+
initialContent: Content<Page>;
|
|
546
|
+
}) {
|
|
547
|
+
// Initialize with server-preloaded data — no loading state needed
|
|
548
|
+
const [pageData, setPageData] = useState(initialContent.data);
|
|
549
|
+
|
|
550
|
+
useEffect(() => {
|
|
551
|
+
if (isSyncEnabled() && isBrowser() && window.localess) {
|
|
552
|
+
window.localess.on(['input', 'change'], (event) => {
|
|
553
|
+
if (event.type === 'input' || event.type === 'change') {
|
|
554
|
+
setPageData(event.data);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
// No cleanup needed: window.localess has no .off() method
|
|
559
|
+
}, []);
|
|
560
|
+
|
|
561
|
+
return (
|
|
562
|
+
<main {...localessEditable(pageData)}>
|
|
563
|
+
{pageData?.body.map(item => (
|
|
564
|
+
<LocalessComponent key={item._id} data={item} links={initialContent.links} references={initialContent.references} />
|
|
331
565
|
))}
|
|
332
566
|
</main>
|
|
333
567
|
);
|
|
@@ -342,7 +576,9 @@ The following are re-exported for convenience so you only need to import from `@
|
|
|
342
576
|
|
|
343
577
|
**Types:** `Content`, `ContentData`, `ContentMetadata`, `ContentDataSchema`, `ContentDataField`, `ContentAsset`, `ContentRichText`, `ContentLink`, `ContentReference`, `Links`, `References`, `Translations`, `LocalessClient`, `LocalessSync`, `EventToApp`, `EventCallback`, `EventToAppType`
|
|
344
578
|
|
|
345
|
-
**Functions:** `localessEditable`, `localessEditableField`, `llEditable` *(deprecated)*, `llEditableField` *(deprecated)*, `isBrowser`, `isServer`, `isIframe`
|
|
579
|
+
**Functions:** `localessEditable`, `localessEditableField`, `llEditable` *(deprecated)*, `llEditableField` *(deprecated)*, `isBrowser`, `isServer`, `isIframe`, `resolveAsset`, `findLink`, `useLocaless`, `renderRichTextToReact`, `localessInit`, `getLocalessClient`, `registerComponent`, `unregisterComponent`, `setComponents`, `getComponent`, `setFallbackComponent`, `getFallbackComponent`, `isSyncEnabled`
|
|
580
|
+
|
|
581
|
+
**Components:** `LocalessComponent`, `LocalessDocument`
|
|
346
582
|
|
|
347
583
|
---
|
|
348
584
|
|
package/SKILL.md
CHANGED
|
@@ -174,22 +174,85 @@ localessInit({
|
|
|
174
174
|
|
|
175
175
|
> Never enable sync in production — the script is only meaningful inside the Localess Visual Editor iframe.
|
|
176
176
|
|
|
177
|
-
###
|
|
177
|
+
### With `useLocaless` Hook
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
The `useLocaless` hook handles the full cycle automatically — initial fetch and live sync updates — with no extra wiring needed.
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
'use client';
|
|
183
|
+
|
|
184
|
+
import { useLocaless, LocalessComponent, localessEditable } from "@localess/react";
|
|
185
|
+
import type { Content, Page } from "./.localess/localess";
|
|
186
|
+
|
|
187
|
+
export function PageClient({ initialContent, locale }: { initialContent: Content<Page>; locale?: string }) {
|
|
188
|
+
const content = useLocaless<Page>('home', { locale }) ?? initialContent;
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<main {...localessEditable(content.data)}>
|
|
192
|
+
{content.data?.body?.map(item => (
|
|
193
|
+
<LocalessComponent
|
|
194
|
+
key={item._id}
|
|
195
|
+
data={item}
|
|
196
|
+
links={content.links}
|
|
197
|
+
references={content.references}
|
|
198
|
+
/>
|
|
199
|
+
))}
|
|
200
|
+
</main>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### With `LocalessDocument` Component
|
|
206
|
+
|
|
207
|
+
`LocalessDocument` is a component alternative to the hook. It accepts server-fetched `data` and manages live sync updates internally, delegating rendering to `LocalessComponent`. Useful when you prefer a component-based approach over hooks.
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
// Server Component — pass fetched data directly to LocalessDocument
|
|
211
|
+
import { getLocalessClient, LocalessDocument } from "@localess/react";
|
|
212
|
+
import type { Page } from "./.localess/localess";
|
|
213
|
+
|
|
214
|
+
export default async function HomePage({ params }: { params: Promise<{ locale?: string }> }) {
|
|
215
|
+
const { locale } = await params;
|
|
216
|
+
const client = getLocalessClient();
|
|
217
|
+
const content = await client.getContentBySlug<Page>('home', { locale });
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<LocalessDocument
|
|
221
|
+
data={content.data}
|
|
222
|
+
links={content.links}
|
|
223
|
+
references={content.references}
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Props** (same shape as `LocalessComponent`):
|
|
230
|
+
|
|
231
|
+
| Prop | Type | Required | Description |
|
|
232
|
+
|------|------|----------|-------------|
|
|
233
|
+
| `data` | `ContentData` | ✅ | Initial content data |
|
|
234
|
+
| `links` | `Links` | ❌ | Resolved links map |
|
|
235
|
+
| `references` | `References` | ❌ | Resolved references map |
|
|
236
|
+
| `ref` | `React.Ref<HTMLElement>` | ❌ | Forwarded to the rendered root element |
|
|
237
|
+
|
|
238
|
+
> Subscribes to `input` / `change` events automatically when `enableSync` is active. Unlike `useLocaless`, it does not fetch content — it only handles live sync for data passed in as props.
|
|
239
|
+
|
|
240
|
+
### Manual Integration
|
|
241
|
+
|
|
242
|
+
If you manage content state yourself without `useLocaless` or `LocalessDocument`, subscribe to editor events directly via `window.localess`:
|
|
180
243
|
|
|
181
244
|
```tsx
|
|
182
245
|
'use client';
|
|
183
246
|
|
|
184
247
|
import { useEffect, useState } from "react";
|
|
185
|
-
import { LocalessComponent, localessEditable } from "@localess/react";
|
|
248
|
+
import { LocalessComponent, localessEditable, isSyncEnabled, isBrowser } from "@localess/react";
|
|
186
249
|
import type { Content, Page } from "./.localess/localess";
|
|
187
250
|
|
|
188
251
|
export function PageClient({ initialContent }: { initialContent: Content<Page> }) {
|
|
189
252
|
const [pageData, setPageData] = useState(initialContent.data);
|
|
190
253
|
|
|
191
254
|
useEffect(() => {
|
|
192
|
-
if (window.localess) {
|
|
255
|
+
if (isSyncEnabled() && isBrowser() && window.localess) {
|
|
193
256
|
window.localess.on(['input', 'change'], (event) => {
|
|
194
257
|
if (event.type === 'input' || event.type === 'change') {
|
|
195
258
|
setPageData(event.data);
|
|
@@ -230,57 +293,97 @@ export function PageClient({ initialContent }: { initialContent: Content<Page> }
|
|
|
230
293
|
|
|
231
294
|
### Pattern: Split Server/Client Components (Next.js App Router)
|
|
232
295
|
|
|
233
|
-
|
|
296
|
+
**Preload data server-side** and pass it to the Client Component. The page renders immediately with server data — no loading flash — and Visual Editor sync kicks in on top.
|
|
297
|
+
|
|
298
|
+
**Server Component** (same for all three options below):
|
|
234
299
|
|
|
235
300
|
```tsx
|
|
236
|
-
// app/[locale]/page.tsx
|
|
301
|
+
// app/[locale]/page.tsx
|
|
237
302
|
import { getLocalessClient } from "@localess/react";
|
|
238
|
-
import {
|
|
239
|
-
import type { Page } from "./.localess/localess";
|
|
303
|
+
import type { Content, Page } from "./.localess/localess";
|
|
240
304
|
|
|
241
305
|
export default async function HomePage({
|
|
242
|
-
|
|
306
|
+
params,
|
|
243
307
|
}: {
|
|
244
|
-
|
|
308
|
+
params: Promise<{ locale?: string }>;
|
|
245
309
|
}) {
|
|
246
|
-
const { locale } = await
|
|
247
|
-
|
|
248
|
-
const content = await
|
|
310
|
+
const { locale } = await params;
|
|
311
|
+
// Data fetched during SSR — preloaded into the client component as a prop
|
|
312
|
+
const content = await getLocalessClient().getContentBySlug<Page>('home', { locale });
|
|
313
|
+
|
|
314
|
+
return <PageClient initialContent={content} locale={locale} />;
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Option A — `useLocaless` hook**: re-fetches on client, falls back to server data until resolved, auto-syncs with editor.
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
// app/[locale]/page-client.tsx
|
|
322
|
+
'use client';
|
|
323
|
+
|
|
324
|
+
import { useLocaless, LocalessComponent, localessEditable } from "@localess/react";
|
|
325
|
+
import type { Content, Page } from "./.localess/localess";
|
|
326
|
+
|
|
327
|
+
export function PageClient({ initialContent, locale }: { initialContent: Content<Page>; locale?: string }) {
|
|
328
|
+
// ?? initialContent: renders server data immediately, switches to hook result once ready
|
|
329
|
+
const content = useLocaless<Page>('home', { locale }) ?? initialContent;
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<main {...localessEditable(content.data)}>
|
|
333
|
+
{content.data?.body?.map(item => (
|
|
334
|
+
<LocalessComponent key={item._id} data={item} links={content.links} references={content.references} />
|
|
335
|
+
))}
|
|
336
|
+
</main>
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Option B — `LocalessDocument` component**: no client re-fetch, uses server data directly, auto-syncs with editor.
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
// app/[locale]/page.tsx — no separate client file needed
|
|
345
|
+
import { getLocalessClient, LocalessDocument } from "@localess/react";
|
|
346
|
+
import type { Page } from "./.localess/localess";
|
|
347
|
+
|
|
348
|
+
export default async function HomePage({ params }: { params: Promise<{ locale?: string }> }) {
|
|
349
|
+
const { locale } = await params;
|
|
350
|
+
const content = await getLocalessClient().getContentBySlug<Page>('home', { locale });
|
|
249
351
|
|
|
250
|
-
return
|
|
352
|
+
return (
|
|
353
|
+
<LocalessDocument data={content.data} links={content.links} references={content.references} />
|
|
354
|
+
);
|
|
251
355
|
}
|
|
252
356
|
```
|
|
253
357
|
|
|
358
|
+
**Option C — Manual**: full control, initialise state with server-preloaded data, subscribe to sync yourself.
|
|
359
|
+
|
|
254
360
|
```tsx
|
|
255
|
-
// app/[locale]/page-client.tsx
|
|
361
|
+
// app/[locale]/page-client.tsx
|
|
256
362
|
'use client';
|
|
257
363
|
|
|
258
364
|
import { useEffect, useState } from "react";
|
|
259
|
-
import { LocalessComponent, localessEditable } from "@localess/react";
|
|
365
|
+
import { LocalessComponent, localessEditable, isSyncEnabled, isBrowser } from "@localess/react";
|
|
260
366
|
import type { Content, Page } from "./.localess/localess";
|
|
261
367
|
|
|
262
368
|
export function PageClient({ initialContent }: { initialContent: Content<Page> }) {
|
|
369
|
+
// Server-preloaded data used as initial state — no loading flash
|
|
263
370
|
const [pageData, setPageData] = useState(initialContent.data);
|
|
264
371
|
|
|
265
372
|
useEffect(() => {
|
|
266
|
-
if (window.localess) {
|
|
373
|
+
if (isSyncEnabled() && isBrowser() && window.localess) {
|
|
267
374
|
window.localess.on(['input', 'change'], (event) => {
|
|
268
375
|
if (event.type === 'input' || event.type === 'change') {
|
|
269
376
|
setPageData(event.data);
|
|
270
377
|
}
|
|
271
378
|
});
|
|
272
379
|
}
|
|
380
|
+
// No cleanup needed: window.localess has no .off() method
|
|
273
381
|
}, []);
|
|
274
382
|
|
|
275
383
|
return (
|
|
276
384
|
<main {...localessEditable(pageData)}>
|
|
277
385
|
{pageData?.body?.map(item => (
|
|
278
|
-
<LocalessComponent
|
|
279
|
-
key={item._id}
|
|
280
|
-
data={item}
|
|
281
|
-
links={initialContent.links}
|
|
282
|
-
references={initialContent.references}
|
|
283
|
-
/>
|
|
386
|
+
<LocalessComponent key={item._id} data={item} links={initialContent.links} references={initialContent.references} />
|
|
284
387
|
))}
|
|
285
388
|
</main>
|
|
286
389
|
);
|
|
@@ -289,6 +392,67 @@ export function PageClient({ initialContent }: { initialContent: Content<Page> }
|
|
|
289
392
|
|
|
290
393
|
---
|
|
291
394
|
|
|
395
|
+
## `useLocaless` Hook
|
|
396
|
+
|
|
397
|
+
`useLocaless<T>` fetches content by slug on the client side and automatically wires up Visual Editor live updates when `enableSync` is active.
|
|
398
|
+
|
|
399
|
+
```tsx
|
|
400
|
+
'use client';
|
|
401
|
+
|
|
402
|
+
import { useLocaless } from "@localess/react";
|
|
403
|
+
import type { Page } from "./.localess/localess";
|
|
404
|
+
|
|
405
|
+
export function PageView({ slug }: { slug: string }) {
|
|
406
|
+
const content = useLocaless<Page>(slug, { locale: 'en' });
|
|
407
|
+
|
|
408
|
+
if (!content) return <div>Loading…</div>;
|
|
409
|
+
|
|
410
|
+
return (
|
|
411
|
+
<main>
|
|
412
|
+
{content.data.body.map(item => (
|
|
413
|
+
<LocalessComponent key={item._id} data={item} links={content.links} />
|
|
414
|
+
))}
|
|
415
|
+
</main>
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Signature
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
useLocaless<T extends ContentData = ContentData>(
|
|
424
|
+
slug: string | string[], // string[] is joined with '/' — e.g. ['blog', 'post'] → 'blog/post'
|
|
425
|
+
options?: ContentFetchParams
|
|
426
|
+
): Content<T> | undefined
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
- Returns `undefined` while the initial fetch is in flight.
|
|
430
|
+
- When `enableSync` is active and the page is inside the Localess Visual Editor, automatically subscribes to `input` / `change` events and updates the returned value in place.
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Link Utilities
|
|
435
|
+
|
|
436
|
+
### `findLink(links, link)`
|
|
437
|
+
|
|
438
|
+
Resolves a `ContentLink` to a URL string. Use this to convert link fields from Localess content into href values.
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
import { findLink } from "@localess/react";
|
|
442
|
+
|
|
443
|
+
const href = findLink(content.links, data.ctaLink);
|
|
444
|
+
// type: 'content' → '/' + fullSlug (e.g. '/blog/my-post'), or '/not-found' if not in links map
|
|
445
|
+
// type: 'url' → raw URI (e.g. 'https://example.com')
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
const NavLink = ({ data, links }) => (
|
|
450
|
+
<a href={findLink(links, data.link)}>{data.label}</a>
|
|
451
|
+
);
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
292
456
|
## Rich Text Rendering
|
|
293
457
|
|
|
294
458
|
Converts Localess `ContentRichText` (Tiptap JSON) to a React node tree.
|
|
@@ -361,7 +525,7 @@ const [content, translations] = await Promise.all([
|
|
|
361
525
|
|
|
362
526
|
---
|
|
363
527
|
|
|
364
|
-
## Full Next.js
|
|
528
|
+
## Full Next.js 16.2 App Router Setup
|
|
365
529
|
|
|
366
530
|
```typescript
|
|
367
531
|
// app/layout.tsx (Server Component)
|
|
@@ -437,9 +601,16 @@ export { setFallbackComponent, getFallbackComponent, isSyncEnabled }
|
|
|
437
601
|
|
|
438
602
|
// Rendering
|
|
439
603
|
export { LocalessComponent } // Dynamic schema-to-component renderer
|
|
604
|
+
export { LocalessDocument } // Schema renderer + built-in Visual Editor sync
|
|
440
605
|
export { renderRichTextToReact } // Rich text → React nodes
|
|
441
606
|
export { resolveAsset } // ContentAsset → full URL
|
|
442
607
|
|
|
608
|
+
// Hooks
|
|
609
|
+
export { useLocaless } // Client-side content fetching with sync support
|
|
610
|
+
|
|
611
|
+
// Utilities
|
|
612
|
+
export { findLink } // ContentLink → URL string
|
|
613
|
+
|
|
443
614
|
// Visual editor (re-exported from @localess/client)
|
|
444
615
|
export { localessEditable, localessEditableField }
|
|
445
616
|
export { llEditable, llEditableField } // Deprecated
|
package/dist/index.d.mts
CHANGED
|
@@ -10,6 +10,13 @@ type LocalessComponentProps<T extends ContentData = ContentData> = {
|
|
|
10
10
|
};
|
|
11
11
|
declare const LocalessComponent: React.ForwardRefExoticComponent<LocalessComponentProps<ContentData> & React.RefAttributes<HTMLElement>>;
|
|
12
12
|
|
|
13
|
+
type LocalessDocumentProps<T extends ContentData = ContentData> = {
|
|
14
|
+
data: T;
|
|
15
|
+
links?: Links;
|
|
16
|
+
references?: References;
|
|
17
|
+
};
|
|
18
|
+
declare const LocalessDocument: React.ForwardRefExoticComponent<LocalessDocumentProps<ContentData> & React.RefAttributes<HTMLElement>>;
|
|
19
|
+
|
|
13
20
|
type LocalessOptions = LocalessClientOptions & {
|
|
14
21
|
/**
|
|
15
22
|
* Components mapping for Localess Component integration
|
|
@@ -91,4 +98,4 @@ declare function isSyncEnabled(): boolean;
|
|
|
91
98
|
*/
|
|
92
99
|
declare function resolveAsset(asset: ContentAsset): string;
|
|
93
100
|
|
|
94
|
-
export { LocalessComponent, type LocalessComponentProps, type LocalessOptions, type UseLocalessOptions, findLink, getComponent, getFallbackComponent, getLocalessClient, isSyncEnabled, localessInit, registerComponent, renderRichTextToReact, resolveAsset, setComponents, setFallbackComponent, unregisterComponent, useLocaless };
|
|
101
|
+
export { LocalessComponent, type LocalessComponentProps, LocalessDocument, type LocalessDocumentProps, type LocalessOptions, type UseLocalessOptions, findLink, getComponent, getFallbackComponent, getLocalessClient, isSyncEnabled, localessInit, registerComponent, renderRichTextToReact, resolveAsset, setComponents, setFallbackComponent, unregisterComponent, useLocaless };
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ type LocalessComponentProps<T extends ContentData = ContentData> = {
|
|
|
10
10
|
};
|
|
11
11
|
declare const LocalessComponent: React.ForwardRefExoticComponent<LocalessComponentProps<ContentData> & React.RefAttributes<HTMLElement>>;
|
|
12
12
|
|
|
13
|
+
type LocalessDocumentProps<T extends ContentData = ContentData> = {
|
|
14
|
+
data: T;
|
|
15
|
+
links?: Links;
|
|
16
|
+
references?: References;
|
|
17
|
+
};
|
|
18
|
+
declare const LocalessDocument: React.ForwardRefExoticComponent<LocalessDocumentProps<ContentData> & React.RefAttributes<HTMLElement>>;
|
|
19
|
+
|
|
13
20
|
type LocalessOptions = LocalessClientOptions & {
|
|
14
21
|
/**
|
|
15
22
|
* Components mapping for Localess Component integration
|
|
@@ -91,4 +98,4 @@ declare function isSyncEnabled(): boolean;
|
|
|
91
98
|
*/
|
|
92
99
|
declare function resolveAsset(asset: ContentAsset): string;
|
|
93
100
|
|
|
94
|
-
export { LocalessComponent, type LocalessComponentProps, type LocalessOptions, type UseLocalessOptions, findLink, getComponent, getFallbackComponent, getLocalessClient, isSyncEnabled, localessInit, registerComponent, renderRichTextToReact, resolveAsset, setComponents, setFallbackComponent, unregisterComponent, useLocaless };
|
|
101
|
+
export { LocalessComponent, type LocalessComponentProps, LocalessDocument, type LocalessDocumentProps, type LocalessOptions, type UseLocalessOptions, findLink, getComponent, getFallbackComponent, getLocalessClient, isSyncEnabled, localessInit, registerComponent, renderRichTextToReact, resolveAsset, setComponents, setFallbackComponent, unregisterComponent, useLocaless };
|
package/dist/index.js
CHANGED
|
@@ -21,16 +21,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
LocalessComponent: () => LocalessComponent,
|
|
24
|
+
LocalessDocument: () => LocalessDocument,
|
|
24
25
|
findLink: () => findLink,
|
|
25
26
|
getComponent: () => getComponent,
|
|
26
27
|
getFallbackComponent: () => getFallbackComponent,
|
|
27
28
|
getLocalessClient: () => getLocalessClient,
|
|
28
|
-
isBrowser: () =>
|
|
29
|
-
isIframe: () =>
|
|
30
|
-
isServer: () =>
|
|
29
|
+
isBrowser: () => import_client5.isBrowser,
|
|
30
|
+
isIframe: () => import_client5.isIframe,
|
|
31
|
+
isServer: () => import_client5.isServer,
|
|
31
32
|
isSyncEnabled: () => isSyncEnabled,
|
|
32
|
-
localessEditable: () =>
|
|
33
|
-
localessEditableField: () =>
|
|
33
|
+
localessEditable: () => import_client5.localessEditable,
|
|
34
|
+
localessEditableField: () => import_client5.localessEditableField,
|
|
34
35
|
localessInit: () => localessInit,
|
|
35
36
|
registerComponent: () => registerComponent,
|
|
36
37
|
renderRichTextToReact: () => renderRichTextToReact,
|
|
@@ -41,7 +42,7 @@ __export(index_exports, {
|
|
|
41
42
|
useLocaless: () => useLocaless
|
|
42
43
|
});
|
|
43
44
|
module.exports = __toCommonJS(index_exports);
|
|
44
|
-
var
|
|
45
|
+
var import_client5 = require("@localess/client");
|
|
45
46
|
|
|
46
47
|
// src/components/localess-component.tsx
|
|
47
48
|
var import_react = require("react");
|
|
@@ -137,11 +138,30 @@ var LocalessComponent = (0, import_react.forwardRef)(({ data, links, references,
|
|
|
137
138
|
] });
|
|
138
139
|
});
|
|
139
140
|
|
|
140
|
-
// src/
|
|
141
|
+
// src/components/localess-document.tsx
|
|
141
142
|
var import_react2 = require("react");
|
|
142
143
|
var import_client3 = require("@localess/client");
|
|
144
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
145
|
+
var LocalessDocument = (0, import_react2.forwardRef)(({ data, links, references, ...restProps }, ref) => {
|
|
146
|
+
const [contentData, setContentData] = (0, import_react2.useState)(data);
|
|
147
|
+
(0, import_react2.useEffect)(() => {
|
|
148
|
+
if (isSyncEnabled() && (0, import_client3.isBrowser)()) {
|
|
149
|
+
window.localess?.on(["input", "change"], (event) => {
|
|
150
|
+
console.log("Localess:event", event);
|
|
151
|
+
if (event.type === "change" || event.type === "input") {
|
|
152
|
+
setContentData(event.data);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LocalessComponent, { data: contentData, links, references, ...restProps });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// src/hooks/use-localess.ts
|
|
161
|
+
var import_react3 = require("react");
|
|
162
|
+
var import_client4 = require("@localess/client");
|
|
143
163
|
var useLocaless = (slug, options = {}) => {
|
|
144
|
-
const [document, setDocument] = (0,
|
|
164
|
+
const [document, setDocument] = (0, import_react3.useState)();
|
|
145
165
|
const client = getLocalessClient();
|
|
146
166
|
let normalizedSlug;
|
|
147
167
|
if (Array.isArray(slug)) {
|
|
@@ -149,11 +169,11 @@ var useLocaless = (slug, options = {}) => {
|
|
|
149
169
|
} else {
|
|
150
170
|
normalizedSlug = slug;
|
|
151
171
|
}
|
|
152
|
-
(0,
|
|
172
|
+
(0, import_react3.useEffect)(() => {
|
|
153
173
|
async function loadDocument() {
|
|
154
174
|
const document2 = await client.getContentBySlug(normalizedSlug, options);
|
|
155
175
|
setDocument(document2);
|
|
156
|
-
if (isSyncEnabled() && (0,
|
|
176
|
+
if (isSyncEnabled() && (0, import_client4.isBrowser)()) {
|
|
157
177
|
window.localess?.on(["input", "change"], (event) => {
|
|
158
178
|
if (event.type === "change" || event.type === "input") {
|
|
159
179
|
setDocument({ ...document2, data: event.data });
|
|
@@ -188,7 +208,7 @@ function findLink(links, link) {
|
|
|
188
208
|
}
|
|
189
209
|
|
|
190
210
|
// src/richtext.ts
|
|
191
|
-
var
|
|
211
|
+
var import_react4 = require("@tiptap/static-renderer/pm/react");
|
|
192
212
|
var import_extension_document = require("@tiptap/extension-document");
|
|
193
213
|
var import_extension_text = require("@tiptap/extension-text");
|
|
194
214
|
var import_extension_paragraph = require("@tiptap/extension-paragraph");
|
|
@@ -205,7 +225,7 @@ var import_extension_code = require("@tiptap/extension-code");
|
|
|
205
225
|
var import_extension_code_block_lowlight = require("@tiptap/extension-code-block-lowlight");
|
|
206
226
|
var import_extension_link = require("@tiptap/extension-link");
|
|
207
227
|
function renderRichTextToReact(content) {
|
|
208
|
-
return (0,
|
|
228
|
+
return (0, import_react4.renderToReactElement)({
|
|
209
229
|
content,
|
|
210
230
|
extensions: [
|
|
211
231
|
import_extension_document.Document,
|
|
@@ -231,6 +251,7 @@ function renderRichTextToReact(content) {
|
|
|
231
251
|
// Annotate the CommonJS export names for ESM import in node:
|
|
232
252
|
0 && (module.exports = {
|
|
233
253
|
LocalessComponent,
|
|
254
|
+
LocalessDocument,
|
|
234
255
|
findLink,
|
|
235
256
|
getComponent,
|
|
236
257
|
getFallbackComponent,
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { localessEditable as localessEditable2, localessEditableField, isBrowser as
|
|
2
|
+
import { localessEditable as localessEditable2, localessEditableField, isBrowser as isBrowser3, isServer, isIframe } from "@localess/client";
|
|
3
3
|
|
|
4
4
|
// src/components/localess-component.tsx
|
|
5
5
|
import { forwardRef } from "react";
|
|
@@ -95,11 +95,30 @@ var LocalessComponent = forwardRef(({ data, links, references, ...restProps }, r
|
|
|
95
95
|
] });
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
-
// src/
|
|
99
|
-
import { useEffect, useState } from "react";
|
|
98
|
+
// src/components/localess-document.tsx
|
|
99
|
+
import { forwardRef as forwardRef2, useEffect, useState } from "react";
|
|
100
100
|
import { isBrowser } from "@localess/client";
|
|
101
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
102
|
+
var LocalessDocument = forwardRef2(({ data, links, references, ...restProps }, ref) => {
|
|
103
|
+
const [contentData, setContentData] = useState(data);
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (isSyncEnabled() && isBrowser()) {
|
|
106
|
+
window.localess?.on(["input", "change"], (event) => {
|
|
107
|
+
console.log("Localess:event", event);
|
|
108
|
+
if (event.type === "change" || event.type === "input") {
|
|
109
|
+
setContentData(event.data);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
return /* @__PURE__ */ jsx2(LocalessComponent, { data: contentData, links, references, ...restProps });
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// src/hooks/use-localess.ts
|
|
118
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
119
|
+
import { isBrowser as isBrowser2 } from "@localess/client";
|
|
101
120
|
var useLocaless = (slug, options = {}) => {
|
|
102
|
-
const [document, setDocument] =
|
|
121
|
+
const [document, setDocument] = useState2();
|
|
103
122
|
const client = getLocalessClient();
|
|
104
123
|
let normalizedSlug;
|
|
105
124
|
if (Array.isArray(slug)) {
|
|
@@ -107,11 +126,11 @@ var useLocaless = (slug, options = {}) => {
|
|
|
107
126
|
} else {
|
|
108
127
|
normalizedSlug = slug;
|
|
109
128
|
}
|
|
110
|
-
|
|
129
|
+
useEffect2(() => {
|
|
111
130
|
async function loadDocument() {
|
|
112
131
|
const document2 = await client.getContentBySlug(normalizedSlug, options);
|
|
113
132
|
setDocument(document2);
|
|
114
|
-
if (isSyncEnabled() &&
|
|
133
|
+
if (isSyncEnabled() && isBrowser2()) {
|
|
115
134
|
window.localess?.on(["input", "change"], (event) => {
|
|
116
135
|
if (event.type === "change" || event.type === "input") {
|
|
117
136
|
setDocument({ ...document2, data: event.data });
|
|
@@ -188,11 +207,12 @@ function renderRichTextToReact(content) {
|
|
|
188
207
|
}
|
|
189
208
|
export {
|
|
190
209
|
LocalessComponent,
|
|
210
|
+
LocalessDocument,
|
|
191
211
|
findLink,
|
|
192
212
|
getComponent,
|
|
193
213
|
getFallbackComponent,
|
|
194
214
|
getLocalessClient,
|
|
195
|
-
|
|
215
|
+
isBrowser3 as isBrowser,
|
|
196
216
|
isIframe,
|
|
197
217
|
isServer,
|
|
198
218
|
isSyncEnabled,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localess/react",
|
|
3
|
-
"version": "3.0.1-dev.
|
|
3
|
+
"version": "3.0.1-dev.20260408183448",
|
|
4
4
|
"description": "ReactJS JavaScript/TypeScript SDK for Localess's API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"localess",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"react-dom": "^17 || ^18 || ^19"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@localess/client": "3.0.1-dev.
|
|
49
|
+
"@localess/client": "3.0.1-dev.20260408183448",
|
|
50
50
|
"@tiptap/static-renderer": "^3.20.1",
|
|
51
51
|
"@tiptap/html": "^3.20.1",
|
|
52
52
|
"@tiptap/extension-bold": "^3.20.1",
|