> ## Documentation Index
> Fetch the complete documentation index at: https://docs.platform.embeddables.com/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 figures out two things on every visit: **which project** the request belongs to, and **who the visitor is** — an anonymous **app user ID** it creates the first time someone visits (no login or account needed) and remembers across sessions. Every other SDK reuses that identity from Core, so it's only set up once.

Core doesn't make any network calls of its own, and it's required by every other 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 your project config with the [Embeddables CLI](/get-started/em-cli), then import the generated output in your app:

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

The generated config does **not** include your `publishableKey` — always add it when initializing (`{ ...config, publishableKey }`). Use the key `em init` printed, or find it under **[Settings → Portal / SDK](https://admin.embeddables.com/settings)** in the admin app.

Install once, then pick your setup:

<Tabs>
  <Tab title="React">
    In your existing React app (React 18 or newer), wrap it once, above every product SDK:

    ```tsx lines 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, publishableKey: 'pk_sandbox_<your-key>' }}
          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` import in the example turns on Analytics, Experiments, and Forms when you use those
    products. If you only need Core for now, you can omit `modules` and add it later. Every Core hook is
    listed under **Hooks reference** below.

    <Info>
      Core finishes setting up **after** your component mounts, so `useEmbeddables()` and
      `useAppUserId()` return `null` on the first render (and during server rendering). Always handle
      that loading state.
    </Info>

    <Warning>
      `config`, `modules`, and `serverAppUserId` only take effect on the first render — the provider
      captures them once and does not pick up changes on later renders. To switch project, product
      modules, or visitor identity later, remount `EmbeddablesProvider` (for example, with a `key`).
    </Warning>

    #### Turning on other SDKs (React)

    **Recommended:** run `em build` after you add SDKs with the CLI. It creates the `modules` file you
    import in the example — already in the right order.

    **Manual:** list each product's React setup yourself (same as the generated file). Put Analytics first
    so Experiments and Forms can use it. Form schemas live in Core config (`config.forms` from the CLI), so
    `forms()` takes no schema argument:

    ```tsx lines theme={null}
    import { analytics } from '@embeddables/analytics/react'
    import { experiments } from '@embeddables/experiments/react'
    import { forms } from '@embeddables/forms/react'
    import type { EmbeddablesReactModule } from '@embeddables/core/react'

    const modules: readonly EmbeddablesReactModule[] = [
      analytics(),
      experiments(),
      forms(),
    ]
    ```

    Include only the factories for the SDKs you use. Each factory takes that SDK's options — for example
    `analytics({ publishableKey })` or `forms({ analyticsInstance })`.

    #### Hooks reference

    Core's React hooks only work inside the wrapper from the example. They may show empty at first while
    Core finishes loading — handle that brief state in your UI. See the
    [reference](/sdks/core/reference#hooks) for `useEmbeddables`, `useAppUserId`, page views, and the
    rest.
  </Tab>

  <Tab title="JavaScript">
    ```typescript lines 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.
  </Tab>

  <Tab title="Server">
    Use this when your page is built on the server first. Core needs a way to read the visitor's cookies
    from that request — it cannot use browser storage:

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

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

    <Warning>
      On the server, Core reads identity from the request cookies you pass through `cookies.get`. In the
      browser it reads `localStorage` only — it does not read the identity cookie at init, so a
      `Set-Cookie` on your response won't hand the id off to the first render. If the server creates a new
      id and you don't pass it to the browser as `serverAppUserId` (see below), the browser makes its own
      separate id, and you end up counting one visitor as two.
    </Warning>

    #### Same visitor on server and browser

    When the server creates a visitor id, pass that same id into the React wrapper as `serverAppUserId`
    (through your app's normal page data). Otherwise the browser may create a second id and look like two
    people in reporting.

    ```typescript lines 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,
        publishableKey: 'pk_sandbox_<your-key>',
        cookies: {
          get(name) {
            return parseCookie(request.headers.get('cookie') ?? '', name)
          },
        },
      })
      return server.getAppUserId()
    }
    ```

    ```tsx lines 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, publishableKey: 'pk_sandbox_<your-key>' }}
          serverAppUserId={appUserId}
          modules={modules}
        >
          <App />
        </EmbeddablesProvider>
      )
    }
    ```

    Setting a cookie in the server response helps later visits, but the first screen in the browser still
    needs `serverAppUserId` to match immediately — the cookie alone is not enough on that first load.
  </Tab>
</Tabs>

<Info>
  Need every method and hook? See [Reference — methods and hooks](/sdks/core/reference#methods).
</Info>

### Do I ever call Core directly?

Usually not much beyond initializing it. Its getters are mostly used by Analytics, Experiments, and Forms behind the scenes — you'll work with those SDKs far more often than with Core itself. The full instance and config surface is catalogued in the [Reference](/sdks/core/reference).
