Skip to content

Conversation

@jacekradko
Copy link
Member

@jacekradko jacekradko commented Jan 28, 2026

Caution

This is a proof of concept / exploration - NOT intended for full review or merge.

This PR demonstrates a potential approach to lightweight version pinning. It is meant to gather feedback on the direction, not to be production-ready code.

Summary

Explores @clerk/clerk-js-pinned and @clerk/ui-pinned packages for dependency-managed version pinning without heavy transitive dependencies.

Problem

Today, users can pin:

  • @clerk/clerk-js via a string prop like clerkJsVersion="x.y.z"
  • @clerk/ui via importing a ui object from @clerk/ui

This is inconsistent and confusing. The naive "make them install @clerk/ui to pin it" approach forces large transitive dependency graphs onto apps that otherwise rely on hot-loaded runtime code.

Proposed Solution

Create lightweight packages that:

  • Export only version metadata and types
  • Have zero runtime dependencies
  • Provide a consistent import surface for pinning both packages
  • Bundle all types inline using dts-bundle-generator (no external type dependencies)

New Packages (POC)

@clerk/clerk-js-pinned (~0.34KB gzipped)

  • Exports version string constant
  • Exports clerkJs branded object for use with new clerkJs prop

@clerk/ui-pinned (~0.41KB gzipped)

  • Exports version string constant
  • Exports ui branded object with full Appearance type support
  • All 1000+ lines of appearance types are bundled inline

Usage

import { clerkJs } from '@clerk/clerk-js-pinned';
import { ui } from '@clerk/ui-pinned';

<ClerkProvider 
  clerkJs={clerkJs}
  ui={ui}
  appearance={{ variables: { colorPrimary: '#000' } }}
  publishableKey="..."
/>

Changes

  • Add @clerk/clerk-js-pinned package
  • Add @clerk/ui-pinned package
  • Add ClerkJs and Ui branded types to @clerk/shared/types
  • Add clerkJs prop to IsomorphicClerkOptions
  • Update loadClerkJsScript to handle clerkJs object

Open Questions

1. Lock-step Versioning

How do we keep these packages in sync with their parent packages?

Proposed approach: Use changesets fixed groups in .changeset/config.json:

"fixed": [
  ["@clerk/clerk-js", "@clerk/clerk-js-pinned"],
  ["@clerk/ui", "@clerk/ui-pinned"]
]

This ensures when @clerk/clerk-js bumps to 5.115.0, @clerk/clerk-js-pinned automatically gets the same version.

Alternative: CI automation that bumps pinned packages when parent packages bump.

2. Migration Strategy (Backward Compatibility)

The POC maintains the old string-based props for easier migration:

For clerk-js (coexistence):

  • clerkJSVersion?: string - existing string-based approach (kept)
  • clerkJs?: { version: string } - new branded object approach (added)

The implementation shows precedence: clerkJs?.version ?? clerkJSVersion

For UI:

  • ui?: { version: string } - new branded object approach (added)

Questions:

  • Should we deprecate clerkJSVersion eventually?
  • How do we handle users who have both props set?
  • What is the deprecation timeline?

3. Other Open Questions

  • Package naming: *-pinned vs something else?
  • Release workflow integration
  • Documentation approach

Introduces @clerk/clerk-js-pinned and @clerk/ui-pinned packages for
dependency-managed version pinning without heavy transitive dependencies.

- Add @clerk/clerk-js-pinned: exports clerkJs branded object and version
- Add @clerk/ui-pinned: exports ui branded object with full Appearance types
- Add ClerkJs and Ui branded types to @clerk/shared
- Add clerkJs prop to IsomorphicClerkOptions
- Update loadClerkJsScript to handle clerkJs object
- Use dts-bundle-generator to bundle all types inline (zero type deps)

Both packages are <1KB gzipped with zero runtime dependencies.
@changeset-bot
Copy link

changeset-bot bot commented Jan 28, 2026

⚠️ No Changeset found

Latest commit: db222d1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Jan 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jan 28, 2026 3:20am

Request Review

@jacekradko jacekradko changed the title feat(*): add lightweight version pinning packages [POC] Lightweight version pinning packages - Do not merge Jan 28, 2026
@jacekradko jacekradko marked this pull request as ready for review January 28, 2026 03:22
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

This change introduces two new pinned packages, @clerk/clerk-js-pinned and @clerk/ui-pinned, along with supporting infrastructure. The packages expose branded version objects enabling consumers to pin specific Clerk.js and Clerk UI versions via dependency imports. Supporting changes include a new branded type system in @clerk/shared (Tagged, ClerkJs, Ui, ExtractAppearanceType), updates to LoadClerkJsScriptOptions and IsomorphicClerkOptions to accept clerkJs properties, and migration of shared type definitions from @clerk/ui to @clerk/shared. Each pinned package includes TypeScript configuration, build scripts via tsdown, and dual ESM/CJS outputs with bundled type definitions.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies this as a POC for lightweight version pinning packages and explicitly marks it as 'Do not merge', directly reflecting the PR's objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/internal/index.ts (1)

1-1: Add a local Ui type import to fix the TypeScript compile error.

Ui is re-exported on line 7 but not imported into local scope, causing a compilation error when used in the localUiForTesting cast on line 68.

🐛 Fix required
 import type { Appearance } from './appearance';
+import type { Ui } from '@clerk/shared/types';

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants