Skip to main content

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

Project config

Generate config with the Embeddables CLI, then import the output in every Core entry point:
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 in the admin app.

Client (browser)

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:
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), the client generates its own UUID on first load and the visitor ends up split across two identities.

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.
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:
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.
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.
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):
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.

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.