> ## Documentation Index
> Fetch the complete documentation index at: https://embeddables-platform.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Core SDK

> @embeddables/core — identity and project configuration every other SDK depends on

## Overview

Core resolves two things on every visit: **which project** the request belongs to, and **who the visitor is** — an anonymous identifier (**app user ID**) Core generates the first time someone visits, with no login or account required, and persists across sessions (cookie/local storage). Every other SDK reads that identity from Core instead of resolving it independently, so it only needs to be established once per session.

Core performs no network calls itself; identity and config resolution are purely local. It is a required dependency for every product SDK on this site.

## Implementation

### Install

<CodeGroup>
  ```bash npm theme={null}
  npm install @embeddables/core
  ```

  ```bash pnpm theme={null}
  pnpm add @embeddables/core
  ```

  ```bash yarn theme={null}
  yarn add @embeddables/core
  ```
</CodeGroup>

### Project config

Generate config with the [Embeddables CLI](/reference/em-cli), then import the output in every Core entry point:

```typescript theme={null}
import { config } from './embeddables/_dist'
```

Add your `publishableKey` when initializing if it is not already in the generated file — use the keys `em init` printed, or find them under **[Settings → Portal / SDK](https://admin.embeddables.com/settings)** in the admin app.

### Client (browser)

```typescript theme={null}
import { initEmbeddables } from '@embeddables/core'
import { config } from './embeddables/_dist'

const embeddables = initEmbeddables({
  ...config,
  publishableKey: 'pk_sandbox_<your-key>',
})

embeddables.getAppUserId() // string
embeddables.getProjectId() // string
embeddables.getPublishableKey() // string | undefined
embeddables.getExperiments() // readonly unknown[] — the experiments array from config
```

`initEmbeddables` returns an `EmbeddablesInstance` — the object every other SDK's `core` option expects. Its read methods are `getAppUserId`, `getProjectId`, `getPublishableKey`, and `getExperiments`.

### Server

Use the server entry point for SSR / Node code. It needs a way to read cookies from the incoming request, since it can't touch `localStorage`:

```typescript theme={null}
import { initEmbeddablesServer } from '@embeddables/core/server'
import { config } from './embeddables/_dist'

const embeddables = initEmbeddablesServer({
  ...config,
  cookies: {
    get(key) {
      return readCookie(key) // your framework's cookie reader
    },
  },
})
```

<Warning>
  On the server, Core reads identity from the request cookies you wire through `cookies.get`. On the
  browser, `initEmbeddables` reads `localStorage` only — it does not parse `Set-Cookie` headers or
  custom response headers by itself. If the server mints a new id and the browser never receives it
  (via `Set-Cookie` **or** an explicit `serverAppUserId` handoff — see [SSR identity
  handoff](#ssr-identity-handoff)), the client generates its own UUID on first load and the visitor
  ends up split across two identities.
</Warning>

### SSR identity handoff

When you render on the server first, read the id from `initEmbeddablesServer`, serialize it in **your** loader or page props, and pass it into `EmbeddablesProvider` as `serverAppUserId`. The provider forwards that value to `initEmbeddables`, which writes it to browser storage before any product SDK runs.

```typescript theme={null}
// Server loader / SSR handler
import { initEmbeddablesServer } from '@embeddables/core/server'
import { config } from './embeddables/_dist'

export function loadAppUserId(request: Request): string {
  const server = initEmbeddablesServer({
    ...config,
    cookies: {
      get(name) {
        return parseCookie(request.headers.get('cookie') ?? '', name)
      },
    },
  })
  return server.getAppUserId()
}
```

```tsx theme={null}
// Client root — your framework passes loaderData / page props however it serializes them
import { EmbeddablesProvider } from '@embeddables/core/react'
import { config } from './embeddables/_dist'
import { modules } from './embeddables/_dist/modules'

export function Root({ appUserId }: { appUserId: string }) {
  return (
    <EmbeddablesProvider config={config} serverAppUserId={appUserId} modules={modules}>
      <App />
    </EmbeddablesProvider>
  )
}
```

You can also set the identity cookie in your SSR response and rely on the browser reading it on the next request — but the **first** client paint after SSR still needs `serverAppUserId` if you want the same id immediately, because browser Core does not re-read response headers during React initialization.

### React

Install the peer dependency and wrap your app once, above every product SDK:

```bash theme={null}
npm install react
```

```tsx theme={null}
import {
  EmbeddablesProvider,
  useAppUserId,
  useEmbeddables,
  useEmbeddablesProjectId,
} from '@embeddables/core/react'
import { config } from './embeddables/_dist'
import { modules } from './embeddables/_dist/modules'

function App() {
  return (
    <EmbeddablesProvider config={config} modules={modules}>
      <Identity />
    </EmbeddablesProvider>
  )
}

function Identity() {
  const embeddables = useEmbeddables()
  const projectId = useEmbeddablesProjectId()
  const appUserId = useAppUserId()

  if (embeddables?.isError) return <p>Embeddables could not initialize.</p>
  if (embeddables === null) return <p>Loading…</p>
  return (
    <p>
      {projectId}: {appUserId}
    </p>
  )
}
```

The `modules` prop registers the product SDKs (Analytics, Experiments, Forms) on the provider — the product hooks on each SDK's page read their client back from it. The CLI generates that list in `embeddables/_dist/modules` from your `config.yaml` when you run `em build`, ordered `analytics → experiments → forms`. If you only use Core, omit `modules`; add it as soon as you enable a product SDK.

`useEmbeddables` is the hook name; `embeddables` is the recommended name for the returned value.

| Hook                        | Returns                                                                             |
| --------------------------- | ----------------------------------------------------------------------------------- |
| `useEmbeddables()`          | Enriched Core instance with `appUserId`, `isError`, and `error`; `null` before init |
| `useEmbeddablesProjectId()` | Project UUID, or `null` before init or on error                                     |
| `useAppUserId()`            | Just the current visitor's id, or `null` before ready                               |

<Info>
  Initialization runs in an effect **after** the component mounts, never during server rendering. So
  `useEmbeddables()` returns `null` and `useAppUserId()` returns `null` on the server render and the
  very first client render — even if you passed `serverAppUserId`. Always handle that loading state;
  don't assume the instance is available immediately.
</Info>

If your server already resolved an identity, pass it down so the client doesn't generate a second one on first paint (see [SSR identity handoff](#ssr-identity-handoff)):

```tsx theme={null}
<EmbeddablesProvider config={config} serverAppUserId={appUserId} modules={modules}>
```

<Warning>
  `config` and `serverAppUserId` are treated as immutable after the first render. Changing them on a
  re-render does nothing — remount `EmbeddablesProvider` (e.g. with a `key`) if you genuinely need
  to switch project or identity.
</Warning>

### Do I ever call Core directly?

Usually not much beyond initializing it. Its getters (`getAppUserId`, `getProjectId`, `getPublishableKey`, `getExperiments`) are mostly consumed internally by Analytics, Experiments, and Forms — you'll interact with those SDKs directly far more often than with Core's own API. Experiments reads `getExperiments()` for its config instead of taking a separate `experiments` option.
