> ## 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.

# Init, methods and hooks

> Config options, instance methods, and React bindings for @embeddables/core

Install, init, and walkthrough examples live in [Overview & setup](/sdks/core/overview). **This page** is the reference for `initEmbeddables` / `initEmbeddablesServer` and [methods and hooks](#methods).

<h2 id="init">
  Set up & use Core
</h2>

In JavaScript, call `initEmbeddables`; on the server, call `initEmbeddablesServer`. Both take your generated project config spread in, plus a `publishableKey`, and return an [`EmbeddablesInstance`](#returns).

<Tabs>
  <Tab title="JavaScript">
    ```typescript theme={null}
    import { initEmbeddables } from '@embeddables/core'
    import { config } from './embeddables/_dist'

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

  <Tab title="Server">
    <span id="server" />

    ```typescript 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) => readCookie(key) },
    })
    ```

    Use this during a server page load (SSR). Identity comes from the `cookies` adapter — there is no
    `localStorage`. Pass the resolved app user id to the client as `serverAppUserId` on
    `initEmbeddables` or `EmbeddablesProvider`.

    <ParamField body="cookies" type="{ get(key: string): string | null }">
      Cookie reader for the incoming request. Core calls `get` with its identity cookie key and expects
      the raw value or `null`. A throwing `get` surfaces as a `StorageError`.
    </ParamField>
  </Tab>
</Tabs>

<ParamField path="options" type="EmbeddablesConfig" required>
  The config object the [Embeddables CLI](/get-started/em-cli) generates, spread in as-is, plus your
  `publishableKey`. Treat the generated config as opaque — don't assemble or edit it by hand.
</ParamField>

<Warning>
  On the server, identity comes from the cookies you pass; in the browser it comes from
  `localStorage`. The browser 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 — it only helps later server requests. If the
  server mints a new id and you don't pass it into the browser as `serverAppUserId`, the browser
  makes its own id and one visitor is counted as two.
</Warning>

<h3 id="methods">
  Methods and hooks
</h3>

**React** covers `EmbeddablesProvider` and the hooks. **JavaScript & Server** lists the shared instance
methods on the object from [`initEmbeddables`](#init) or [`initEmbeddablesServer`](#server), plus
`getCookie` (server only).

<Tabs>
  <Tab title="React">
    <span id="hooks" />

    <AccordionGroup>
      <Accordion title="EmbeddablesProvider" description="Root wrapper — not a hook">
        ```tsx theme={null}
        import { EmbeddablesProvider } from '@embeddables/core/react'

        <EmbeddablesProvider
          config={{ ...config, publishableKey: 'pk_sandbox_<your-key>' }}
          modules={modules}
          serverAppUserId={appUserIdFromSSR}
        >
          {children}
        </EmbeddablesProvider>
        ```

        Initializes Core in an effect after mount, runs each `modules` entry, then publishes the ready
        state so hooks see every registered product client. Children can render once before Core is ready
        — handle that loading state. `config`, `modules`, and `serverAppUserId` are captured on the
        **first** render only — remount with a `key` to change them.

        <ParamField body="config" type="EmbeddablesConfig" required>
          Generated project config spread in, plus `publishableKey`.
        </ParamField>

        <ParamField body="modules" type="readonly EmbeddablesReactModule[]">
          Optional product SDK modules (`analytics()`, `experiments()`, `forms()`, …) from each SDK's
          React entry point. Order matters — keep `analytics` before Experiments and Forms when all three
          are present.
        </ParamField>

        <ParamField body="serverAppUserId" type="string">
          App user id from `initEmbeddablesServer` during SSR so the browser does not mint a second id on
          first load.
        </ParamField>
      </Accordion>

      <Accordion title="useEmbeddables()" description="The enriched Core instance, or null before ready">
        ```tsx theme={null}
        const embeddables = useEmbeddables()
        if (embeddables === null) return <p>Loading…</p>
        if (embeddables.isError) return <p>Could not initialize.</p>
        embeddables.getAppUserId()
        ```

        <ResponseField name="value" type="EmbeddablesReactValue | null">
          `null` before init; otherwise `{ appUserId, isError, error }` plus `getAppUserId`, `getProjectId`,
          `getPublishableKey`, and `getExperiments`. On failure, `isError` is `true` and `appUserId` is
          `null`.
        </ResponseField>
      </Accordion>

      <Accordion title="useAppUserId()" description="Just the visitor id">
        ```tsx theme={null}
        const appUserId = useAppUserId() // string | null
        ```

        <ResponseField name="appUserId" type="string | null">
          The current visitor's id, or `null` before Core is ready.
        </ResponseField>
      </Accordion>

      <Accordion title="useEmbeddablesProjectId()" description="Just the project UUID">
        ```tsx theme={null}
        const projectId = useEmbeddablesProjectId() // string | null
        ```

        <ResponseField name="projectId" type="string | null">
          The project UUID, or `null` before init or on error.
        </ResponseField>
      </Accordion>

      <Accordion title="useRegisterPageView()" description="Record page views from React">
        ```tsx theme={null}
        const { registerPageView, getLastPageView, isPending, isError, error } = useRegisterPageView()
        await registerPageView({ pageKey: 'checkout', isFunnelStep: true })
        ```

        Never throws — an invalid page key or a failing store surfaces as `isError`.

        <ResponseField name="registerPageView" type="function">
          Same input as [registerPageView()](#methods). Returns `undefined` (without error) before Core is
          ready or when the call failed.
        </ResponseField>

        <ResponseField name="getLastPageView" type="function">
          Returns the last recorded page key, or `null` when none is stored or Core is not ready.
        </ResponseField>

        <ResponseField name="isPending" type="boolean">
          `true` while a page view is being recorded.
        </ResponseField>

        <ResponseField name="isError" type="boolean">
          `true` when the latest call failed.
        </ResponseField>

        <ResponseField name="error" type="unknown">
          Details from the failed call, when `isError` is true.
        </ResponseField>
      </Accordion>

      <Accordion title="useEmbeddablesModule({ key })" description="Read a registered product client (advanced)">
        ```tsx theme={null}
        const analytics = useEmbeddablesModule<AnalyticsClient>({ key: 'analytics' })
        ```

        The one place product SDKs read back the client their module registered. Most apps use each SDK's
        own hook instead of calling this directly.

        <ParamField body="key" type="string" required>
          The module key to look up.
        </ParamField>

        <ResponseField name="client" type="TClient | null">
          The registered client, or `null` before Core is ready or when no module claimed the key.
        </ResponseField>
      </Accordion>
    </AccordionGroup>
  </Tab>

  <Tab title="JavaScript & Server">
    <span id="methods" />

    <AccordionGroup>
      <Accordion title="initEmbeddables()" description="JavaScript entry point — reads identity from localStorage">
        ```typescript theme={null}
        import { initEmbeddables } from '@embeddables/core'
        import { config } from './embeddables/_dist'

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

        Resolves the visitor's app user id from `localStorage`, creating and persisting a new one the
        first time.

        <ParamField body="serverAppUserId" type="string">
          An app user id produced by `initEmbeddablesServer` during SSR. When provided, the browser adopts
          it instead of minting a new one on first load.
        </ParamField>
      </Accordion>

      <Accordion title="getAppUserId()" description="The current visitor's id">
        ```typescript theme={null}
        embeddables.getAppUserId() // "usr_abc123"
        ```

        <ResponseField name="appUserId" type="string" required>
          The resolved anonymous visitor id. Never `null` on an instance — resolution happens at init.
        </ResponseField>
      </Accordion>

      <Accordion title="getProjectId()" description="The project UUID">
        ```typescript theme={null}
        embeddables.getProjectId() // "8f2b…"
        ```

        <ResponseField name="projectId" type="string" required>
          The validated project UUID passed at init.
        </ResponseField>
      </Accordion>

      <Accordion title="getPublishableKey()" description="The resolved publishable key, if any">
        ```typescript theme={null}
        embeddables.getPublishableKey() // "pk_sandbox_…" | undefined
        ```

        <ResponseField name="publishableKey" type="string | undefined">
          The key from config or the `EMBEDDABLES_PUBLISHABLE_KEY` environment variable, or `undefined`
          when neither supplied one.
        </ResponseField>
      </Accordion>

      <Accordion title="getExperiments()" description="The validated experiments array">
        ```typescript theme={null}
        embeddables.getExperiments() // readonly unknown[]
        ```

        <ResponseField name="experiments" type="readonly unknown[]">
          The experiment list from config, as validated at init. Shape is not specified here — consume it
          through [Experiments](/sdks/experiments/overview).
        </ResponseField>
      </Accordion>

      <Accordion title="getFormIds()" description="Registered form ids, in config order">
        ```typescript theme={null}
        embeddables.getFormIds() // ["intake", "checkout"]
        ```

        <ResponseField name="formIds" type="readonly string[]">
          The keys of the `forms` map, in insertion order.
        </ResponseField>
      </Accordion>

      <Accordion title="getFormSchema(formId)" description="One form's schema, or undefined">
        ```typescript theme={null}
        embeddables.getFormSchema('intake') // CoreFormSchema | undefined
        ```

        <ParamField path="formId" type="string" required>
          Id of a form registered in config.
        </ParamField>

        <ResponseField name="schema" type="CoreFormSchema | undefined">
          The schema (`id`, optional `name`, and a `fields` array of `{ key, label, type, … }`), or
          `undefined` when no form matches.
        </ResponseField>
      </Accordion>

      <Accordion title="registerPageView(input)" description="Record a page view and optionally emit page:viewed">
        ```typescript theme={null}
        await embeddables.registerPageView({
          pageKey: 'checkout',
          isFunnelStep: true,
          funnelStepLabel: 'Checkout',
        })
        ```

        Always writes the last page view to storage; only emits an analytics `page:viewed` event when an
        [Analytics](/sdks/analytics/overview) client is registered on the same instance. In React, prefer
        [`useRegisterPageView`](#hooks).

        <ParamField body="pageKey" type="string" required>
          Identifier for the page or step (1–128 characters).
        </ParamField>

        <ParamField body="isFunnelStep" type="boolean">
          Mark this view as a funnel step. Omitted from the event rather than sent empty.
        </ParamField>

        <ParamField body="funnelStepLabel" type="string">
          Human-readable step name (1–128 characters), meaningful when `isFunnelStep` is true.
        </ParamField>

        <ResponseField name="result" type="Promise<RegisterPageViewResult>" required>
          Resolves with `{ ok: true }`. If the view is persisted but the analytics `page:viewed` event
          fails to send, the result also carries `trackError: unknown` — the emit never throws. An invalid
          `pageKey` or `funnelStepLabel` rejects the promise instead.
        </ResponseField>
      </Accordion>

      <Accordion title="getLastPageView()" description="The last recorded page key">
        ```typescript theme={null}
        embeddables.getLastPageView() // "checkout" | null
        ```

        <ResponseField name="pageKey" type="string | null">
          The most recent page key — from in-memory storage in the browser, or from the identity cookie on
          the server instance — or `null` when none is stored.
        </ResponseField>
      </Accordion>

      <Accordion title="getCookie(key)" description="Server only — read a request cookie through the adapter">
        ```typescript theme={null}
        embeddables.getCookie('EMBEDDABLES--…--APP-USER-ID') // string | null
        ```

        **Server only.** Available on instances from [`initEmbeddablesServer`](#server); on a browser
        instance from `initEmbeddables` it always returns `null`.

        <ParamField path="key" type="string" required>
          Cookie name to read through the `cookies` adapter passed to `initEmbeddablesServer`.
        </ParamField>

        <ResponseField name="value" type="string | null">
          The cookie value, or `null` when no adapter was provided or the cookie is missing.
        </ResponseField>
      </Accordion>
    </AccordionGroup>

    <Note>
      `setAnalyticsInstance` and `getAnalyticsInstance` also exist on the instance but are composition
      wiring — the [Analytics](/sdks/analytics/overview) module registers its client through them so
      `registerPageView` can emit events. You do not call them directly.
    </Note>
  </Tab>
</Tabs>

<h2 id="returns">
  Returns
</h2>

`initEmbeddables` and `initEmbeddablesServer` return an `EmbeddablesInstance` — the object every other SDK's `core` option expects.

<ResponseField name="EmbeddablesInstance" type="object" required>
  Exposes the read methods documented under [Methods and hooks](#methods): `getAppUserId`,
  `getProjectId`, `getPublishableKey`, `getExperiments`, `getFormIds`, `getFormSchema`, `getCookie`
  (server), `registerPageView`, and `getLastPageView`.
</ResponseField>

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

embeddables.getAppUserId() // "usr_abc123"
embeddables.getProjectId() // "8f2b…"
embeddables.getPublishableKey() // "pk_sandbox_…" | undefined
embeddables.getExperiments() // readonly unknown[]
```
