How to set up Embeddable in a Yarn/Nx monorepo

  1. Monorepo baseline (Yarn workspaces + Nx)
  • Your repo uses Yarn workspaces, managed by Nx.

  • All packages extend a root tsconfig.json and target modern ESM:

{ "compilerOptions": { "target": "ES6", "module": "esnext", "moduleResolution": "node" } }

  1. Add an Embeddable app/package
  • Create a new workspace package (e.g. @your/embeddable) based on the Embeddable Starter Basics or Vanilla Components examples.

  • Wire it into Nx for dev/build so you can use caching and a single command surface.

This standalone setup should run cleanly in the monorepo.

Just so you know

  • You may see the SDK complain about not finding a TypeScript compiler. Running the command with --force is a safe workaround and is unrelated to the integrations below.
  1. Start consuming shared code

When you’re ready to import code from other workspace packages, add a workspace dep and (optionally) a path alias.

package.json

{ "name": "@your/embeddable", "dependencies": { "@your/insights": "workspace:*" } }

tsconfig.json

{ "compilerOptions": { "paths": { "@your/insights": ["path/to/insights"] } } }

Just so you know (and how to work around it)

When introducing tsconfig paths and workspace dependencies, some setups hit module-resolution edge cases (e.g., “can’t import from directory,” “module not found,” or CommonJS interop errors from deep deps). If you see that:

Workaround A — Preferred: feed Embeddable clean ESM builds

  • Prebundle the shared package(s) you import into ESM output (e.g., .es.js) using your bundler of choice (Rollup, tsup, Vite build).

  • Point your emb.ts imports at those built ESM entry points.

  • In Nx, make your Embeddable build depend on the shared package build (so ESM outputs exist first).

This keeps responsibility clear: your shared packages output ESM; Embeddable then bundles those ESM inputs.

Workaround B — Smooth CJS interop when needed
If some private/first-party deps are CommonJS:

  • In the Embeddable package tsconfig.json, enable:

    { "compilerOptions": { "esModuleInterop": true, "allowSyntheticDefaultImports": true } }

  • Prefer ESM builds of your internal libs where possible, or export dual targets (module ESM + main CJS) and import the ESM entry.

Workaround C — Resolve path alias reliably

  • Point alias paths to the built ESM dist of the workspace package (e.g., dist/insights/index.es.js) rather than raw source, so Embeddable sees standard ESM files.

  • Alternatively, remove the alias and import via the package name (after building), which avoids path resolution quirks.

  1. Nx target wiring (example)

Have the shared libs build first, then Embeddable:

// apps/embeddable/project.json { "targets": { "build": { "executor": "nx:run-commands", "options": { "command": "embeddable build --force" }, "dependsOn": ["^build"] // builds upstream libs first }, "dev": { "executor": "nx:run-commands", "options": { "command": "embeddable dev --force" }, "dependsOn": ["^build"] } } }

  1. Keep bundles lean

Prebundling per component can bloat size. A few easy wins:

  • Bundle per package, not per component (one ESM entry that re-exports components).

  • Mark packages tree-shakable ("sideEffects": false where valid).

  • Import named exports so Embeddable can treeshake.

  • Let Nx cache builds to avoid unnecessary rebuilds.