> For the complete Lynkow documentation index in agent-friendly format, see [llms.txt](/llms.txt).

# Getting Started

**Publié le** : 2026-08-20
**Catégorie** : Services

# SDK Functions

## `browserOnly`

```typescript
function browserOnly(fn: () => T, fallback: T): T
```

Execute a function only in browser environment
Returns the fallback value in server environment

| Parameter | Type | Description |
| --- | --- | --- |
| `fn` | `() => T` | Function to execute in browser |
| `fallback` | `T` | Value to return in server environment |


Returns: `T`

```typescript
const visitorId = browserOnly(
  () => localStorage.getItem('visitor_id'),
  null
)
```

---

## `browserOnlyAsync`

```typescript
function browserOnlyAsync(fn: () => Promise<T>, fallback: T): Promise<T>
```

Execute an async function only in browser environment
Returns the fallback value in server environment

| Parameter | Type | Description |
| --- | --- | --- |
| `fn` | `() => Promise<T>` | Async function to execute in browser |
| `fallback` | `T` | Value to return in server environment |


Returns: `Promise<T>`

```typescript
const config = await browserOnlyAsync(
  () => fetchConfig(),
  defaultConfig
)
```

---

## `createClient`

```typescript
function createClient(config: ClientConfig): Client
```

Creates a Lynkow client instance (SDK v3)

| Parameter | Type | Description |
| --- | --- | --- |
| `config` | `ClientConfig` | Client configuration |


Returns: `Client`

```typescript
const lynkow = createClient({
  siteId: 'your-site-uuid',
  locale: 'fr',
  debug: true
})

const posts = await lynkow.contents.list()
```

---

## `createLynkowClient`

```typescript
function createLynkowClient(config: LynkowConfig): LynkowClient
```

Creates a Lynkow client instance (legacy naming)

| Parameter | Type | Description |
| --- | --- | --- |
| `config` | `LynkowConfig` | Client configuration |


Returns: `LynkowClient`

```typescript
// Deprecated -- use createClient() instead:
const lynkow = createClient({ siteId: 'your-site-uuid', locale: 'fr' })
const posts = await lynkow.contents.list()
```

---

## `detectSiteTheme`

```typescript
function detectSiteTheme(): "light" | "dark"
```

Detect the website's theme by inspecting DOM indicators.

Detection cascade:

1. data-theme / data-mode / data-color-scheme attributes on <html> or <body>
2. "dark" class on <html> or <body> (Tailwind convention)
3. CSS color-scheme property on <html>
4. Background color luminance of <body>
5. Fallback: OS-level prefers-color-scheme media query

Returns: `"light" | "dark"`

```typescript
// Apply a conditional class based on the site's current theme
const theme = detectSiteTheme()
document.body.classList.add(theme === 'dark' ? 'inverted-text' : 'default-text')
```

---

## `isCategoryResolve`

```typescript
function isCategoryResolve(response: ResolveResponse): response
```

Checks if a resolution response is a category

| Parameter | Type | Description |
| --- | --- | --- |
| `response` | `ResolveResponse` |  |


Returns: `response`

```typescript
// Resolve a URL path and render the appropriate template
const resolved = await lynkow.paths.resolve('/blog/tutorials')
if (isCategoryResolve(resolved)) {
  renderCategoryPage(resolved.data) // TypeScript narrows to CategoryResolveResponse
}
```

---

## `isContentResolve`

```typescript
function isContentResolve(response: ResolveResponse): response
```

Checks if a resolution response is a content

| Parameter | Type | Description |
| --- | --- | --- |
| `response` | `ResolveResponse` |  |


Returns: `response`

```typescript
// Resolve a URL path and render the appropriate template
const resolved = await lynkow.paths.resolve('/blog/my-article')
if (isContentResolve(resolved)) {
  renderArticle(resolved.data) // TypeScript narrows to ContentResolveResponse
}
```

---

## `isLynkowError`

```typescript
function isLynkowError(error: unknown): error
```

Type guard to check if an unknown value is a `LynkowError`.
Use this in `catch` blocks to safely access `LynkowError` properties.

| Parameter | Type | Description |
| --- | --- | --- |
| `error` | `unknown` | The caught value to check |


Returns: `error`

```typescript
try {
  await lynkow.contents.getBySlug('not-found')
} catch (error) {
  if (isLynkowError(error) && error.code === 'NOT_FOUND') {
    // Handle 404 -- error.status is 404
  }
}
```

---

## `mergeIntoGraph`

```typescript
function mergeIntoGraph(server: object[] | null | undefined, custom: object[] | null | undefined): object[]
```

Concatenate the server-resolved `@graph` with custom client-side nodes
(e.g. ad-hoc Review entries derived from props your Next.js page already
has, or per-page schema.org additions that don't fit the cascade).

Use this instead of a raw `[...server, ...custom]` spread so:

- `null` / `undefined` inputs are tolerated, returning a defensively
empty array — the result is always safe to pass to renderJsonLdGraph.
- `@id` collisions between the server graph and your custom nodes are
detected and emit a `console.warn`. Google merges schema.org entities
that share an `@id`, which can produce duplicate properties (e.g. two
`aggregateRating` on a single LocalBusiness) and downgrade rich
results. The recommended pattern is to prefix custom `@id` values with
the page URL (e.g. `'https://example.com/reviews#review-42'`) so they
are guaranteed unique site-wide.

The function does not deduplicate, reorder, or rewrite ids - it only
concatenates and warns. The intent is to keep the merge transparent so
you can debug the resulting graph by reading top to bottom.

| Parameter | Type | Description |
| --- | --- | --- |
| `server` | `object[] \| null \| undefined` | Server-resolved graph from `content.structuredData?.graph`<br>                or `page.structuredData?.graph`. `null` / `undefined`<br>                treated as an empty array. |
| `custom` | `object[] \| null \| undefined` | Additional nodes you want to inject. Each entry should<br>                be a fully-formed schema.org object (with at least<br>                `@type` and a unique `@id`). `null` / `undefined`<br>                treated as an empty array. |


Returns: `object[]`

```typescript
import { mergeIntoGraph, renderJsonLdGraph } from 'lynkow'
import { lynkow } from '@/lib/lynkow'

export default async function ReviewsPage() {
  const page = await lynkow.pages.getBySlug('reviews')
  const customNodes = [
    {
      '@context': 'https://schema.org',
      '@id': 'https://example.com/reviews#breadcrumb-extra',
      '@type': 'BreadcrumbList',
      'itemListElement': [ ],
    },
  ]
  const merged = mergeIntoGraph(page.structuredData?.graph, customNodes)
  return <div dangerouslySetInnerHTML={{ __html: renderJsonLdGraph(merged) }} />
}
```

---

## `onSiteThemeChange`

```typescript
function onSiteThemeChange(callback: (theme: "light" | "dark") => void): () => void
```

Observe site theme changes in real-time.

Watches for:

- Attribute changes on <html> and <body> (data-theme, data-mode, class, style)
- OS-level prefers-color-scheme media query changes

| Parameter | Type | Description |
| --- | --- | --- |
| `callback` | `(theme: "light" \| "dark") => void` | Called with the new theme when a change is detected |


Returns: `() => void`

```typescript
// Update a widget's appearance when the site theme changes
const stopObserving = onSiteThemeChange((theme) => {
  widget.setTheme(theme)
})

// Stop observing when no longer needed
stopObserving()
```

---

## `renderJsonLdGraph`

```typescript
function renderJsonLdGraph(nodes: object[] | null | undefined): string
```

Render a resolved JSON-LD `@graph` as a single `<script type="application/ld+json">`
tag ready to inject into a page `<head>`.

The input is the array returned by the Lynkow public API at
`content.structuredData.graph` (for articles) or `page.structuredData.graph`
(for site blocks of type page). Each entry is already a fully-formed
schema.org object with `@context`, `@id`, and `@type`. This helper strips
the per-node `@context` and wraps the array in a top-level `@context` +
`@graph` to keep the emitted script as compact as possible.

No HTML escaping is performed on the JSON body itself, but `</script>`
sequences that would otherwise break the enclosing tag are guarded against
via a Unicode-safe replacement.

| Parameter | Type | Description |
| --- | --- | --- |
| `nodes` | `object[] \| null \| undefined` | Array of JSON-LD node objects. `null`, `undefined`, or an<br>               empty array returns an empty string so you can safely<br>               spread the result into server-rendered HTML without<br>               conditional branches. |


Returns: `string`

```typescript
// Next.js App Router (server component)
import { createClient, renderJsonLdGraph } from 'lynkow'

const client = createClient({ siteId: process.env.LYNKOW_SITE_ID! })

export default async function ArticlePage({ params }: { params: { slug: string } }) {
  const article = await client.contents.getBySlug(params.slug)
  return (
    <>
      <div
        dangerouslySetInnerHTML={{
          __html: renderJsonLdGraph(article.structuredData?.graph),
        }}
      />
      <h1>{article.title}</h1>
      <article dangerouslySetInnerHTML={{ __html: article.body }} />
    </>
  )
}
```