# Clerk Monorepo

> Work effectively in the clerk/javascript SDK monorepo. Use when setting up the repo, building / testing / running a package, deciding which of the @clerk/* packages to change, writing changesets, conventional commits, or PRs, or checking whether a change is a breaking change to clerk-js or ui. Covers the pnpm + turbo dev loop, the package map, and the repo's hard rules. AGENTS.md is the authority on the rules; this skill is the how-to layer that points back to it.

- Skill: `clerk/clerk-monorepo` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add clerk/clerk-monorepo`
- Raw SKILL.md: https://api.skillmd.com/api/skills/clerk/clerk-monorepo/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: clerk (https://skillmd.com/u/clerk)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/clerk/clerk-monorepo

---


# Working in the clerk/javascript monorepo

This is Clerk's JavaScript SDK monorepo: 24 packages (21 published `@clerk/*` plus the private
`@clerk/msw`, `@clerk/headless`, and `@clerk/swingset`) managed with pnpm
workspaces and Turborepo. Read this before building, testing, committing, or touching anything
under `packages/`.

`AGENTS.md` (repo root) is the canonical source of truth for the hard rules. This skill restates
those rules in actionable form and links back to it. If a rule here ever disagrees with `AGENTS.md`,
`AGENTS.md` wins, and the discrepancy should be fixed here.

## Fast setup (happy path)

The order matters more than the commands. Do them in sequence:

1. **Node `>=24.15.0`** (pinned in `.nvmrc`). `nvm use` if you have nvm. Wrong Node version is the
   single most common cause of cryptic "cannot find module @clerk/..." build errors.
2. **`corepack enable`** before installing. The `preinstall` hook runs `only-allow pnpm`; npm/yarn
   are hard-blocked. Corepack pins the right pnpm (`>=10.33.0`).
3. **`pnpm install` from the repo root** (never a subdirectory). It is a workspace; installing from
   a package dir leaves cross-package links broken.
4. **`pnpm build`** before anything else. Packages depend on each other's built `dist/` + `.d.ts`.
   Skipping this makes `dev`, tests, and the editor's types all report phantom errors.
5. **`pnpm dev`** to start watch mode.

Full sequence, the 11 footguns, and the internal integration-test / 1Password setup live in
[`references/setup-and-footguns.md`](references/setup-and-footguns.md).

## Package map: where does X live?

The ~10 packages people touch most. Full 24-package table, the dependency pyramid, and the complete
"change X, touch Y" routing are in [`references/package-map.md`](references/package-map.md).

| Package                | You change it when...                                                                                                                                       |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@clerk/shared`        | Utilities used everywhere (storage, events, React helpers). Most-depended-on; changes fan out to ~20 packages. Types live here too (`@clerk/shared/types`). |
| `@clerk/backend`       | Server-side: JWT verification, the Backend API REST client, webhooks. Used by every framework adapter.                                                      |
| `@clerk/clerk-js`      | ⚠️ The browser runtime loaded via script tag. **Backwards-compat sensitive** (see rules).                                                                   |
| `@clerk/ui`            | ⚠️ The React components powering the hosted sign-in / sign-up UI. **Backwards-compat sensitive**.                                                           |
| `@clerk/react`         | Shared React hooks/context (`useAuth`, `useUser`, ...) consumed by the React-based adapters.                                                                |
| `@clerk/nextjs`        | Next.js SDK: middleware, route handlers, server components.                                                                                                 |
| `@clerk/express`       | Express middleware and server helpers.                                                                                                                      |
| `@clerk/expo`          | React Native / Expo SDK.                                                                                                                                    |
| `@clerk/localizations` | UI translation strings (consumed by `ui`).                                                                                                                  |
| `@clerk/testing`       | E2E helpers for consumers (Playwright / Cypress).                                                                                                           |

> Heads-up: `packages/` may contain stale leftover dirs (`types`, `remix`, `themes`, `elements`, ...)
> with only build artifacts and no `package.json`. Those are removed packages, not active ones. The
> authoritative list is the git-tracked `packages/*/package.json` files.

## Dev-loop recipes

```bash
# Build one package (and its deps, via turbo ^build)
pnpm turbo build --filter=@clerk/nextjs

# Watch subsets instead of everything
pnpm dev:fe-libs      # clerk-js + ui + shared
pnpm dev:js           # clerk-js only
pnpm dev:sandbox      # rspack sandbox for previewing UI components

# Run one package's unit tests (builds the package and its deps first)
pnpm turbo test --filter=@clerk/backend
# Faster, after a full build, for tight iteration:
pnpm --filter @clerk/backend test

# Run a single test file (vitest matches by filename substring). No `--` before the path:
# pnpm forwards a literal `--` into the script and vitest then ignores the filter.
pnpm --filter @clerk/shared test path/to/file.test.ts
# @clerk/backend runs a multi-runtime suite (run-s), so target one runtime for a single file:
pnpm --filter @clerk/backend test:node path/to/file.test.ts

# Quality gates (run before pushing; CI runs equivalent checks)
pnpm lint
pnpm format            # workspace packages plus root files, docs/, integration/, scripts/
pnpm prettier --write '.claude/**/*.md'   # pnpm format does not cover .claude/; format skill files this way

# Changesets — write the file, don't run the CLI (both scripts are interactive prompts)
pnpm changeset status --since=origin/main   # what CI checks; run this to verify
```

Write `.changeset/<descriptive-name>.md` directly, in the **repo root** `.changeset/` (never
`packages/<pkg>/.changeset/`). Package change:

```md
---
'@clerk/nextjs': patch
---

Remove the redundant `https://*.client.protect.clerk.com` source from CSP headers generated by `clerkMiddleware()`.
```

Repo/tooling-only or private-package-only change — the file is exactly two delimiters, nothing else:

```md
---
---
```

`@clerk/swingset`, `@clerk/msw`, and `@clerk/headless` are private and never appear in a changeset;
every other `packages/*` entry publishes, **including `@clerk/ui` and `@clerk/clerk-js`**. Bump
selection, which packages to list, body-writing rules, and the major-version gate are in
[`references/changesets.md`](references/changesets.md).

Test runner differs by package (`shared`, `clerk-js`, most adapters use vitest; `backend` runs a
multi-runtime suite), but the `pnpm --filter <name> test` invocation is uniform.

If the editor or a build reports stale types from `@clerk/shared`, rebuild the foundations:
`pnpm turbo build --filter=@clerk/shared`.

Integration-test variants (`pnpm test:integration:*`) and canary/snapshot releases are the long tail:
see [`references/setup-and-footguns.md`](references/setup-and-footguns.md) and `docs/CONTRIBUTING.md`.

## The hard rules

Each rule below restates `AGENTS.md`; the parenthetical is how it is enforced.

- **pnpm only, Node `>=24.15`, pnpm `>=10.33`.** (`preinstall` blocks npm/yarn; `engines` in
  `package.json`.)
- **Every PR needs a changeset.** Write the file directly; the `pnpm changeset` /
  `pnpm changeset:empty` scripts are interactive prompts an agent cannot drive. A changeset is a
  changelog entry for users upgrading, not a summary of the diff. See
  [`references/changesets.md`](references/changesets.md). (CI runs
  `pnpm changeset status --since=origin/main` and fails PRs missing one.)
- **Conventional commit `type(scope):`, scope is mandatory.** Enforced on the **PR title**
  (`.github/workflows/pr-title-linter.yml`), not on individual commits. There is no local
  `commit-msg` hook. Valid `scope` = any `packages/*` short name **and** its `clerk-`-stripped form
  (so `clerk-js` accepts `clerk-js` or `js`), plus `repo`, `release`, `e2e`, `ci`, `*`. `docs` is a
  valid **type**, not a scope. Source of truth: `commitlint.config.ts`.
- **`clerk-js` and `ui` must stay backwards-compatible across non-major releases.** A new `clerk-js`
  runtime loads into apps still pinned to an _older_ framework SDK (`@clerk/nextjs`, etc.), so
  removing or renaming anything an older SDK calls breaks those apps in production. (`break-check`
  flags API-surface changes in `api-changes.yml`, but that check is informational; shipping such a
  change means a `major`, gated by `major-version-check.yml`.)
- **Changes to the core `Clerk` class API (`packages/clerk-js/src/core/clerk.ts`) require a major
  version** and `!allow-major` approval. (`.github/workflows/major-version-check.yml`.) APIs prefixed
  `__internal_` or exported from an `/experimental` subpath are exempt from SemVer guarantees.

## PR / changeset / commit flow

1. Branch off `main`.
2. Make the change in the right package(s); add/update unit tests next to the code.
3. Write `.changeset/<name>.md` (see above, and
   [`references/changesets.md`](references/changesets.md)), then `git add` it. An unstaged changeset
   fails CI the same as a missing one.
4. Verify locally: `pnpm build`, `pnpm test` (or the filtered forms above), `pnpm lint`,
   `pnpm format:check`.
5. Open the PR; the title must be a valid conventional commit (it becomes the squash commit). Fill in
   the PR template and add nothing beyond its sections — in particular, never write a "Testing" /
   "Test plan" / "How to test" section listing the tests added or the checks run. The Checklist
   covers that and reviewers read the diff. Describe the change, not the work done on it.

Release _policy_ (when/how things ship, canary, snapshot, backports) is in `docs/PUBLISH.md`. This
skill stops at opening the PR.

## Breaking-change quick check

If you are editing `clerk-js` or `ui`, answer these. **Any "yes" means it is breaking**, needs a
major + `!allow-major`, and `break-check` will flag it:

1. Removing or renaming a public export, method, or property?
2. Changing a public function/method signature (new required arg, changed return type)?
3. Changing the `Clerk` class public surface in `core/clerk.ts`?
4. Renaming/removing something an older SDK version still calls at runtime?

If the symbol is `__internal_`/`__experimental_`-prefixed or under `/experimental`, it is exempt.
Full decision matrix:
[`references/breaking-changes.md`](references/breaking-changes.md).

## Deeper references

- `AGENTS.md`: the canonical hard rules (authority for this skill).
- `docs/CONTRIBUTING.md`: full setup, testing, JSDoc/Typedoc, changeset writing.
- `docs/PUBLISH.md`: release process (stable, canary, snapshot, backport, `!allow-major`).
- `docs/CICD.md`: CI/CD pipeline and automated releases.
- `docs/SECURITY.md`: vulnerability reporting (do **not** open public issues).
- `references/theming-architecture.md` (repo root, not this skill's `references/`): deep dive on the
  `@clerk/ui` appearance/theming system.
- Bundled: [`setup-and-footguns.md`](references/setup-and-footguns.md),
  [`package-map.md`](references/package-map.md),
  [`breaking-changes.md`](references/breaking-changes.md),
  [`changesets.md`](references/changesets.md).

Analyzing or coordinating a **release PR** (the "Version packages" PR) is out of scope for this
skill; the release process lives in `docs/PUBLISH.md` and `docs/CICD.md`. Clerk employees may also
have dedicated `analyze-javascript-release` / `coordinate-clerk-release` skills installed globally,
but those are not shipped in this repo.

