- 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" } }
- 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.
- 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.
- 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"] } } }
- 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.