Skip to content

feat(ui): add composed profile shell, providers infra and stub router - #9133

Merged
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell
Jul 16, 2026
Merged

feat(ui): add composed profile shell, providers infra and stub router#9133
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stack 4/7. New (unexported) infra for the composed profile API: ProfileProviderShell, stubRouter, useBillingRouter, useRequirePage, PageContext, createSection, plus the shared APIKeysSection/BillingSection used by both User and Org composed dirs.

Nothing is exported from the package yet.

Changesets live only on the release PR #9144; this PR carries none.


Stack

Reviewed as a stack. The 7 PRs merge bottom-up into release branch #9144, which integrates into main as a single unit.

#PRLayer
#9144release → main (integration)
1#9130moduleManager getter across build boundary
2#9131shared profile UI infra
3#9132extract Section components from profile pages
4#9133 👈composed shell + providers infra
5#9134composed UserProfile
6#9135composed OrganizationProfile
7#9136expose @clerk/ui/experimental (public API)

@changeset-bot

changeset-botBot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f47096

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

@coderabbitai

coderabbitaiBot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f50660d-6bcb-44f4-aa04-f1bd5bd56718

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@vercel

vercelBot commented Jul 10, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 12:05pm
swingsetReadyReadyPreview, CommentJul 16, 2026 12:05pm

Request Review

@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 6ff5709 to cb7fd8aCompareJuly 13, 2026 14:08
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 411027c to 9c65deeCompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from cb7fd8a to 13f68a7CompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 9c65dee to e791dc6CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 13f68a7 to 669a561CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 669a561 to 8e09a06CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from e791dc6 to 5cb97b6CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenter marked this pull request as ready for review July 15, 2026 15:04
}
}

export function useBillingRouter(): { router: RouteContextValue; route: BillingRoute } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this a pattern we're going to need elsewhere? We have the VirtualRouter component already, which could potentially be usable here in some fashion, but that one is sort of geared towards modal routing so might not slot in as cleanly.

Food for thought, don't think we need to adjust now but I expect to see this pattern emerge more than once.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Agreed it'''ll recur. Not reusing VirtualRouter: that'''s BaseRouter matching, but composed is leaf-only (stubRouter throws on /matches). Typed in-memory state is the intended shape here. Added a comment noting why. Extract when a 2nd composed router shows up.

export function useRequirePage(componentName: string): boolean {
const page = useContext(PageContext);
if (!page) {
if (typeof __DEV__ === 'undefined' || __DEV__) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would only throw when the package is being developed, not in Clerk dev mode. I think this is intentional, but calling it out regardless

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Good catch — was dead for consumers (__DEV__ baked false in published @clerk/ui). Now gated on isDevelopmentSDK(clerk), same as createCustomPages: throws in consumer dev, null in prod. Throw tests mark the fixture as a dev SDK.

Comment on lines +4 to +12
const store = new WeakMap<object, EmotionCache>();
// their own. The cache is stored with the nonce/cssLayerName it was built from so a
// later change to either rebuilds it (matching the AIO StyleCacheProvider) instead
// of pinning whatever the first-mounted root happened to see.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure if this is really needed. I would recommend that the comment is trimmed down to more clearly explain why this construct exists, or removed.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Trimmed to 3 lines.

Comment on lines +153 to +154
// Match the portal path's normalization (Components.tsx:209) so a cssLayerName
// nested inside appearance.theme gets hoisted to top-level for @layer wrapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this reference file / line no. will get out of date quickly

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Dropped the file:line ref.

Comment on lines +71 to +73
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Compressed to one line (kept a terse why for the cast) + dropped the duplicate type-def comment above it.

… on nonce/layer
Consolidate the duplicated environment + module-manager reads from the composed
providers into resolveComposedClerkRuntime, dropping the `as any` casts for
narrow structural types and warning once on version skew instead of a silent
blank render. Store the shared emotion cache with the nonce/cssLayerName it was
built from so a later change rebuilds it (matching the AIO StyleCacheProvider).
Collapse the multi-line SAFETY/why blocks in ProfileProviderShell and
styleCacheStore to terse single lines, drop the brittle Components.tsx:209
file/line reference, and note why composed billing uses a typed in-memory
router instead of VirtualRouter/BaseRouter.
__DEV__ is baked to false in the published @clerk/ui, so useRequirePage's
misuse error never reached consumers. Gate it on isDevelopmentSDK(clerk) — the
same helper createCustomPages uses — so it throws in the consumer's dev SDK and
degrades to a null render in production.
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 3e7ace2 to 5f47096CompareJuly 16, 2026 12:02
@alexcarpenter
alexcarpenter merged commit 9c7a1a0 into carp/profile-releaseJul 16, 2026
7 of 10 checks passed
@alexcarpenter
alexcarpenter deleted the carp/profile-stack-04-composed-shell branch July 16, 2026 12:03
Sign up for freeto 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

@alexcarpenter@brkalow
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(ui): add composed profile shell, providers infra and stub router by alexcarpenter · Pull Request #9133 · clerk/javascript · GitHub
Skip to content

feat(ui): add composed profile shell, providers infra and stub router - #9133

Merged
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell
Jul 16, 2026
Merged

feat(ui): add composed profile shell, providers infra and stub router#9133
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stack 4/7. New (unexported) infra for the composed profile API: ProfileProviderShell, stubRouter, useBillingRouter, useRequirePage, PageContext, createSection, plus the shared APIKeysSection/BillingSection used by both User and Org composed dirs.

Nothing is exported from the package yet.

Changesets live only on the release PR #9144; this PR carries none.


Stack

Reviewed as a stack. The 7 PRs merge bottom-up into release branch #9144, which integrates into main as a single unit.

#PRLayer
#9144release → main (integration)
1#9130moduleManager getter across build boundary
2#9131shared profile UI infra
3#9132extract Section components from profile pages
4#9133 👈composed shell + providers infra
5#9134composed UserProfile
6#9135composed OrganizationProfile
7#9136expose @clerk/ui/experimental (public API)

@changeset-bot

changeset-botBot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f47096

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

@coderabbitai

coderabbitaiBot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f50660d-6bcb-44f4-aa04-f1bd5bd56718

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@vercel

vercelBot commented Jul 10, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 12:05pm
swingsetReadyReadyPreview, CommentJul 16, 2026 12:05pm

Request Review

@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 6ff5709 to cb7fd8aCompareJuly 13, 2026 14:08
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 411027c to 9c65deeCompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from cb7fd8a to 13f68a7CompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 9c65dee to e791dc6CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 13f68a7 to 669a561CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 669a561 to 8e09a06CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from e791dc6 to 5cb97b6CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenter marked this pull request as ready for review July 15, 2026 15:04
}
}

export function useBillingRouter(): { router: RouteContextValue; route: BillingRoute } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this a pattern we're going to need elsewhere? We have the VirtualRouter component already, which could potentially be usable here in some fashion, but that one is sort of geared towards modal routing so might not slot in as cleanly.

Food for thought, don't think we need to adjust now but I expect to see this pattern emerge more than once.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Agreed it'''ll recur. Not reusing VirtualRouter: that'''s BaseRouter matching, but composed is leaf-only (stubRouter throws on /matches). Typed in-memory state is the intended shape here. Added a comment noting why. Extract when a 2nd composed router shows up.

export function useRequirePage(componentName: string): boolean {
const page = useContext(PageContext);
if (!page) {
if (typeof __DEV__ === 'undefined' || __DEV__) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would only throw when the package is being developed, not in Clerk dev mode. I think this is intentional, but calling it out regardless

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Good catch — was dead for consumers (__DEV__ baked false in published @clerk/ui). Now gated on isDevelopmentSDK(clerk), same as createCustomPages: throws in consumer dev, null in prod. Throw tests mark the fixture as a dev SDK.

Comment on lines +4 to +12
const store = new WeakMap<object, EmotionCache>();
// their own. The cache is stored with the nonce/cssLayerName it was built from so a
// later change to either rebuilds it (matching the AIO StyleCacheProvider) instead
// of pinning whatever the first-mounted root happened to see.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure if this is really needed. I would recommend that the comment is trimmed down to more clearly explain why this construct exists, or removed.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Trimmed to 3 lines.

Comment on lines +153 to +154
// Match the portal path's normalization (Components.tsx:209) so a cssLayerName
// nested inside appearance.theme gets hoisted to top-level for @layer wrapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this reference file / line no. will get out of date quickly

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Dropped the file:line ref.

Comment on lines +71 to +73
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Compressed to one line (kept a terse why for the cast) + dropped the duplicate type-def comment above it.

… on nonce/layer
Consolidate the duplicated environment + module-manager reads from the composed
providers into resolveComposedClerkRuntime, dropping the `as any` casts for
narrow structural types and warning once on version skew instead of a silent
blank render. Store the shared emotion cache with the nonce/cssLayerName it was
built from so a later change rebuilds it (matching the AIO StyleCacheProvider).
Collapse the multi-line SAFETY/why blocks in ProfileProviderShell and
styleCacheStore to terse single lines, drop the brittle Components.tsx:209
file/line reference, and note why composed billing uses a typed in-memory
router instead of VirtualRouter/BaseRouter.
__DEV__ is baked to false in the published @clerk/ui, so useRequirePage's
misuse error never reached consumers. Gate it on isDevelopmentSDK(clerk) — the
same helper createCustomPages uses — so it throws in the consumer's dev SDK and
degrades to a null render in production.
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 3e7ace2 to 5f47096CompareJuly 16, 2026 12:02
@alexcarpenter
alexcarpenter merged commit 9c7a1a0 into carp/profile-releaseJul 16, 2026
7 of 10 checks passed
@alexcarpenter
alexcarpenter deleted the carp/profile-stack-04-composed-shell branch July 16, 2026 12:03
Sign up for freeto 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

@alexcarpenter@brkalow
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(ui): add composed profile shell, providers infra and stub router by alexcarpenter · Pull Request #9133 · clerk/javascript · GitHub
Skip to content

feat(ui): add composed profile shell, providers infra and stub router - #9133

Merged
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell
Jul 16, 2026
Merged

feat(ui): add composed profile shell, providers infra and stub router#9133
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stack 4/7. New (unexported) infra for the composed profile API: ProfileProviderShell, stubRouter, useBillingRouter, useRequirePage, PageContext, createSection, plus the shared APIKeysSection/BillingSection used by both User and Org composed dirs.

Nothing is exported from the package yet.

Changesets live only on the release PR #9144; this PR carries none.


Stack

Reviewed as a stack. The 7 PRs merge bottom-up into release branch #9144, which integrates into main as a single unit.

#PRLayer
#9144release → main (integration)
1#9130moduleManager getter across build boundary
2#9131shared profile UI infra
3#9132extract Section components from profile pages
4#9133 👈composed shell + providers infra
5#9134composed UserProfile
6#9135composed OrganizationProfile
7#9136expose @clerk/ui/experimental (public API)

@changeset-bot

changeset-botBot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f47096

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

@coderabbitai

coderabbitaiBot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f50660d-6bcb-44f4-aa04-f1bd5bd56718

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@vercel

vercelBot commented Jul 10, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 12:05pm
swingsetReadyReadyPreview, CommentJul 16, 2026 12:05pm

Request Review

@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 6ff5709 to cb7fd8aCompareJuly 13, 2026 14:08
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 411027c to 9c65deeCompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from cb7fd8a to 13f68a7CompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 9c65dee to e791dc6CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 13f68a7 to 669a561CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 669a561 to 8e09a06CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from e791dc6 to 5cb97b6CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenter marked this pull request as ready for review July 15, 2026 15:04
}
}

export function useBillingRouter(): { router: RouteContextValue; route: BillingRoute } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this a pattern we're going to need elsewhere? We have the VirtualRouter component already, which could potentially be usable here in some fashion, but that one is sort of geared towards modal routing so might not slot in as cleanly.

Food for thought, don't think we need to adjust now but I expect to see this pattern emerge more than once.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Agreed it'''ll recur. Not reusing VirtualRouter: that'''s BaseRouter matching, but composed is leaf-only (stubRouter throws on /matches). Typed in-memory state is the intended shape here. Added a comment noting why. Extract when a 2nd composed router shows up.

export function useRequirePage(componentName: string): boolean {
const page = useContext(PageContext);
if (!page) {
if (typeof __DEV__ === 'undefined' || __DEV__) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would only throw when the package is being developed, not in Clerk dev mode. I think this is intentional, but calling it out regardless

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Good catch — was dead for consumers (__DEV__ baked false in published @clerk/ui). Now gated on isDevelopmentSDK(clerk), same as createCustomPages: throws in consumer dev, null in prod. Throw tests mark the fixture as a dev SDK.

Comment on lines +4 to +12
const store = new WeakMap<object, EmotionCache>();
// their own. The cache is stored with the nonce/cssLayerName it was built from so a
// later change to either rebuilds it (matching the AIO StyleCacheProvider) instead
// of pinning whatever the first-mounted root happened to see.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure if this is really needed. I would recommend that the comment is trimmed down to more clearly explain why this construct exists, or removed.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Trimmed to 3 lines.

Comment on lines +153 to +154
// Match the portal path's normalization (Components.tsx:209) so a cssLayerName
// nested inside appearance.theme gets hoisted to top-level for @layer wrapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this reference file / line no. will get out of date quickly

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Dropped the file:line ref.

Comment on lines +71 to +73
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Compressed to one line (kept a terse why for the cast) + dropped the duplicate type-def comment above it.

… on nonce/layer
Consolidate the duplicated environment + module-manager reads from the composed
providers into resolveComposedClerkRuntime, dropping the `as any` casts for
narrow structural types and warning once on version skew instead of a silent
blank render. Store the shared emotion cache with the nonce/cssLayerName it was
built from so a later change rebuilds it (matching the AIO StyleCacheProvider).
Collapse the multi-line SAFETY/why blocks in ProfileProviderShell and
styleCacheStore to terse single lines, drop the brittle Components.tsx:209
file/line reference, and note why composed billing uses a typed in-memory
router instead of VirtualRouter/BaseRouter.
__DEV__ is baked to false in the published @clerk/ui, so useRequirePage's
misuse error never reached consumers. Gate it on isDevelopmentSDK(clerk) — the
same helper createCustomPages uses — so it throws in the consumer's dev SDK and
degrades to a null render in production.
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 3e7ace2 to 5f47096CompareJuly 16, 2026 12:02
@alexcarpenter
alexcarpenter merged commit 9c7a1a0 into carp/profile-releaseJul 16, 2026
7 of 10 checks passed
@alexcarpenter
alexcarpenter deleted the carp/profile-stack-04-composed-shell branch July 16, 2026 12:03
Sign up for freeto 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

@alexcarpenter@brkalow
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(ui): add composed profile shell, providers infra and stub router by alexcarpenter · Pull Request #9133 · clerk/javascript · GitHub
Skip to content

feat(ui): add composed profile shell, providers infra and stub router - #9133

Merged
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell
Jul 16, 2026
Merged

feat(ui): add composed profile shell, providers infra and stub router#9133
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stack 4/7. New (unexported) infra for the composed profile API: ProfileProviderShell, stubRouter, useBillingRouter, useRequirePage, PageContext, createSection, plus the shared APIKeysSection/BillingSection used by both User and Org composed dirs.

Nothing is exported from the package yet.

Changesets live only on the release PR #9144; this PR carries none.


Stack

Reviewed as a stack. The 7 PRs merge bottom-up into release branch #9144, which integrates into main as a single unit.

#PRLayer
#9144release → main (integration)
1#9130moduleManager getter across build boundary
2#9131shared profile UI infra
3#9132extract Section components from profile pages
4#9133 👈composed shell + providers infra
5#9134composed UserProfile
6#9135composed OrganizationProfile
7#9136expose @clerk/ui/experimental (public API)

@changeset-bot

changeset-botBot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f47096

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

@coderabbitai

coderabbitaiBot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f50660d-6bcb-44f4-aa04-f1bd5bd56718

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@vercel

vercelBot commented Jul 10, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 12:05pm
swingsetReadyReadyPreview, CommentJul 16, 2026 12:05pm

Request Review

@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 6ff5709 to cb7fd8aCompareJuly 13, 2026 14:08
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 411027c to 9c65deeCompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from cb7fd8a to 13f68a7CompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 9c65dee to e791dc6CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 13f68a7 to 669a561CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 669a561 to 8e09a06CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from e791dc6 to 5cb97b6CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenter marked this pull request as ready for review July 15, 2026 15:04
}
}

export function useBillingRouter(): { router: RouteContextValue; route: BillingRoute } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this a pattern we're going to need elsewhere? We have the VirtualRouter component already, which could potentially be usable here in some fashion, but that one is sort of geared towards modal routing so might not slot in as cleanly.

Food for thought, don't think we need to adjust now but I expect to see this pattern emerge more than once.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Agreed it'''ll recur. Not reusing VirtualRouter: that'''s BaseRouter matching, but composed is leaf-only (stubRouter throws on /matches). Typed in-memory state is the intended shape here. Added a comment noting why. Extract when a 2nd composed router shows up.

export function useRequirePage(componentName: string): boolean {
const page = useContext(PageContext);
if (!page) {
if (typeof __DEV__ === 'undefined' || __DEV__) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would only throw when the package is being developed, not in Clerk dev mode. I think this is intentional, but calling it out regardless

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Good catch — was dead for consumers (__DEV__ baked false in published @clerk/ui). Now gated on isDevelopmentSDK(clerk), same as createCustomPages: throws in consumer dev, null in prod. Throw tests mark the fixture as a dev SDK.

Comment on lines +4 to +12
const store = new WeakMap<object, EmotionCache>();
// their own. The cache is stored with the nonce/cssLayerName it was built from so a
// later change to either rebuilds it (matching the AIO StyleCacheProvider) instead
// of pinning whatever the first-mounted root happened to see.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure if this is really needed. I would recommend that the comment is trimmed down to more clearly explain why this construct exists, or removed.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Trimmed to 3 lines.

Comment on lines +153 to +154
// Match the portal path's normalization (Components.tsx:209) so a cssLayerName
// nested inside appearance.theme gets hoisted to top-level for @layer wrapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this reference file / line no. will get out of date quickly

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Dropped the file:line ref.

Comment on lines +71 to +73
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Compressed to one line (kept a terse why for the cast) + dropped the duplicate type-def comment above it.

… on nonce/layer
Consolidate the duplicated environment + module-manager reads from the composed
providers into resolveComposedClerkRuntime, dropping the `as any` casts for
narrow structural types and warning once on version skew instead of a silent
blank render. Store the shared emotion cache with the nonce/cssLayerName it was
built from so a later change rebuilds it (matching the AIO StyleCacheProvider).
Collapse the multi-line SAFETY/why blocks in ProfileProviderShell and
styleCacheStore to terse single lines, drop the brittle Components.tsx:209
file/line reference, and note why composed billing uses a typed in-memory
router instead of VirtualRouter/BaseRouter.
__DEV__ is baked to false in the published @clerk/ui, so useRequirePage's
misuse error never reached consumers. Gate it on isDevelopmentSDK(clerk) — the
same helper createCustomPages uses — so it throws in the consumer's dev SDK and
degrades to a null render in production.
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 3e7ace2 to 5f47096CompareJuly 16, 2026 12:02
@alexcarpenter
alexcarpenter merged commit 9c7a1a0 into carp/profile-releaseJul 16, 2026
7 of 10 checks passed
@alexcarpenter
alexcarpenter deleted the carp/profile-stack-04-composed-shell branch July 16, 2026 12:03
Sign up for freeto 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

@alexcarpenter@brkalow
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(ui): add composed profile shell, providers infra and stub router by alexcarpenter · Pull Request #9133 · clerk/javascript · GitHub
Skip to content

feat(ui): add composed profile shell, providers infra and stub router - #9133

Merged
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell
Jul 16, 2026
Merged

feat(ui): add composed profile shell, providers infra and stub router#9133
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stack 4/7. New (unexported) infra for the composed profile API: ProfileProviderShell, stubRouter, useBillingRouter, useRequirePage, PageContext, createSection, plus the shared APIKeysSection/BillingSection used by both User and Org composed dirs.

Nothing is exported from the package yet.

Changesets live only on the release PR #9144; this PR carries none.


Stack

Reviewed as a stack. The 7 PRs merge bottom-up into release branch #9144, which integrates into main as a single unit.

#PRLayer
#9144release → main (integration)
1#9130moduleManager getter across build boundary
2#9131shared profile UI infra
3#9132extract Section components from profile pages
4#9133 👈composed shell + providers infra
5#9134composed UserProfile
6#9135composed OrganizationProfile
7#9136expose @clerk/ui/experimental (public API)

@changeset-bot

changeset-botBot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f47096

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

@coderabbitai

coderabbitaiBot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f50660d-6bcb-44f4-aa04-f1bd5bd56718

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@vercel

vercelBot commented Jul 10, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 12:05pm
swingsetReadyReadyPreview, CommentJul 16, 2026 12:05pm

Request Review

@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 6ff5709 to cb7fd8aCompareJuly 13, 2026 14:08
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 411027c to 9c65deeCompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from cb7fd8a to 13f68a7CompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 9c65dee to e791dc6CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 13f68a7 to 669a561CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 669a561 to 8e09a06CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from e791dc6 to 5cb97b6CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenter marked this pull request as ready for review July 15, 2026 15:04
}
}

export function useBillingRouter(): { router: RouteContextValue; route: BillingRoute } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this a pattern we're going to need elsewhere? We have the VirtualRouter component already, which could potentially be usable here in some fashion, but that one is sort of geared towards modal routing so might not slot in as cleanly.

Food for thought, don't think we need to adjust now but I expect to see this pattern emerge more than once.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Agreed it'''ll recur. Not reusing VirtualRouter: that'''s BaseRouter matching, but composed is leaf-only (stubRouter throws on /matches). Typed in-memory state is the intended shape here. Added a comment noting why. Extract when a 2nd composed router shows up.

export function useRequirePage(componentName: string): boolean {
const page = useContext(PageContext);
if (!page) {
if (typeof __DEV__ === 'undefined' || __DEV__) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would only throw when the package is being developed, not in Clerk dev mode. I think this is intentional, but calling it out regardless

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Good catch — was dead for consumers (__DEV__ baked false in published @clerk/ui). Now gated on isDevelopmentSDK(clerk), same as createCustomPages: throws in consumer dev, null in prod. Throw tests mark the fixture as a dev SDK.

Comment on lines +4 to +12
const store = new WeakMap<object, EmotionCache>();
// their own. The cache is stored with the nonce/cssLayerName it was built from so a
// later change to either rebuilds it (matching the AIO StyleCacheProvider) instead
// of pinning whatever the first-mounted root happened to see.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure if this is really needed. I would recommend that the comment is trimmed down to more clearly explain why this construct exists, or removed.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Trimmed to 3 lines.

Comment on lines +153 to +154
// Match the portal path's normalization (Components.tsx:209) so a cssLayerName
// nested inside appearance.theme gets hoisted to top-level for @layer wrapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this reference file / line no. will get out of date quickly

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Dropped the file:line ref.

Comment on lines +71 to +73
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Compressed to one line (kept a terse why for the cast) + dropped the duplicate type-def comment above it.

… on nonce/layer
Consolidate the duplicated environment + module-manager reads from the composed
providers into resolveComposedClerkRuntime, dropping the `as any` casts for
narrow structural types and warning once on version skew instead of a silent
blank render. Store the shared emotion cache with the nonce/cssLayerName it was
built from so a later change rebuilds it (matching the AIO StyleCacheProvider).
Collapse the multi-line SAFETY/why blocks in ProfileProviderShell and
styleCacheStore to terse single lines, drop the brittle Components.tsx:209
file/line reference, and note why composed billing uses a typed in-memory
router instead of VirtualRouter/BaseRouter.
__DEV__ is baked to false in the published @clerk/ui, so useRequirePage's
misuse error never reached consumers. Gate it on isDevelopmentSDK(clerk) — the
same helper createCustomPages uses — so it throws in the consumer's dev SDK and
degrades to a null render in production.
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 3e7ace2 to 5f47096CompareJuly 16, 2026 12:02
@alexcarpenter
alexcarpenter merged commit 9c7a1a0 into carp/profile-releaseJul 16, 2026
7 of 10 checks passed
@alexcarpenter
alexcarpenter deleted the carp/profile-stack-04-composed-shell branch July 16, 2026 12:03
Sign up for freeto 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

@alexcarpenter@brkalow
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(ui): add composed profile shell, providers infra and stub router by alexcarpenter · Pull Request #9133 · clerk/javascript · GitHub
Skip to content

feat(ui): add composed profile shell, providers infra and stub router - #9133

Merged
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell
Jul 16, 2026
Merged

feat(ui): add composed profile shell, providers infra and stub router#9133
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stack 4/7. New (unexported) infra for the composed profile API: ProfileProviderShell, stubRouter, useBillingRouter, useRequirePage, PageContext, createSection, plus the shared APIKeysSection/BillingSection used by both User and Org composed dirs.

Nothing is exported from the package yet.

Changesets live only on the release PR #9144; this PR carries none.


Stack

Reviewed as a stack. The 7 PRs merge bottom-up into release branch #9144, which integrates into main as a single unit.

#PRLayer
#9144release → main (integration)
1#9130moduleManager getter across build boundary
2#9131shared profile UI infra
3#9132extract Section components from profile pages
4#9133 👈composed shell + providers infra
5#9134composed UserProfile
6#9135composed OrganizationProfile
7#9136expose @clerk/ui/experimental (public API)

@changeset-bot

changeset-botBot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f47096

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

@coderabbitai

coderabbitaiBot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f50660d-6bcb-44f4-aa04-f1bd5bd56718

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@vercel

vercelBot commented Jul 10, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 12:05pm
swingsetReadyReadyPreview, CommentJul 16, 2026 12:05pm

Request Review

@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 6ff5709 to cb7fd8aCompareJuly 13, 2026 14:08
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 411027c to 9c65deeCompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from cb7fd8a to 13f68a7CompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 9c65dee to e791dc6CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 13f68a7 to 669a561CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 669a561 to 8e09a06CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from e791dc6 to 5cb97b6CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenter marked this pull request as ready for review July 15, 2026 15:04
}
}

export function useBillingRouter(): { router: RouteContextValue; route: BillingRoute } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this a pattern we're going to need elsewhere? We have the VirtualRouter component already, which could potentially be usable here in some fashion, but that one is sort of geared towards modal routing so might not slot in as cleanly.

Food for thought, don't think we need to adjust now but I expect to see this pattern emerge more than once.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Agreed it'''ll recur. Not reusing VirtualRouter: that'''s BaseRouter matching, but composed is leaf-only (stubRouter throws on /matches). Typed in-memory state is the intended shape here. Added a comment noting why. Extract when a 2nd composed router shows up.

export function useRequirePage(componentName: string): boolean {
const page = useContext(PageContext);
if (!page) {
if (typeof __DEV__ === 'undefined' || __DEV__) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would only throw when the package is being developed, not in Clerk dev mode. I think this is intentional, but calling it out regardless

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Good catch — was dead for consumers (__DEV__ baked false in published @clerk/ui). Now gated on isDevelopmentSDK(clerk), same as createCustomPages: throws in consumer dev, null in prod. Throw tests mark the fixture as a dev SDK.

Comment on lines +4 to +12
const store = new WeakMap<object, EmotionCache>();
// their own. The cache is stored with the nonce/cssLayerName it was built from so a
// later change to either rebuilds it (matching the AIO StyleCacheProvider) instead
// of pinning whatever the first-mounted root happened to see.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure if this is really needed. I would recommend that the comment is trimmed down to more clearly explain why this construct exists, or removed.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Trimmed to 3 lines.

Comment on lines +153 to +154
// Match the portal path's normalization (Components.tsx:209) so a cssLayerName
// nested inside appearance.theme gets hoisted to top-level for @layer wrapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this reference file / line no. will get out of date quickly

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Dropped the file:line ref.

Comment on lines +71 to +73
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Compressed to one line (kept a terse why for the cast) + dropped the duplicate type-def comment above it.

… on nonce/layer
Consolidate the duplicated environment + module-manager reads from the composed
providers into resolveComposedClerkRuntime, dropping the `as any` casts for
narrow structural types and warning once on version skew instead of a silent
blank render. Store the shared emotion cache with the nonce/cssLayerName it was
built from so a later change rebuilds it (matching the AIO StyleCacheProvider).
Collapse the multi-line SAFETY/why blocks in ProfileProviderShell and
styleCacheStore to terse single lines, drop the brittle Components.tsx:209
file/line reference, and note why composed billing uses a typed in-memory
router instead of VirtualRouter/BaseRouter.
__DEV__ is baked to false in the published @clerk/ui, so useRequirePage's
misuse error never reached consumers. Gate it on isDevelopmentSDK(clerk) — the
same helper createCustomPages uses — so it throws in the consumer's dev SDK and
degrades to a null render in production.
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 3e7ace2 to 5f47096CompareJuly 16, 2026 12:02
@alexcarpenter
alexcarpenter merged commit 9c7a1a0 into carp/profile-releaseJul 16, 2026
7 of 10 checks passed
@alexcarpenter
alexcarpenter deleted the carp/profile-stack-04-composed-shell branch July 16, 2026 12:03
Sign up for freeto 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

@alexcarpenter@brkalow
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(ui): add composed profile shell, providers infra and stub router by alexcarpenter · Pull Request #9133 · clerk/javascript · GitHub
Skip to content

feat(ui): add composed profile shell, providers infra and stub router - #9133

Merged
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell
Jul 16, 2026
Merged

feat(ui): add composed profile shell, providers infra and stub router#9133
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stack 4/7. New (unexported) infra for the composed profile API: ProfileProviderShell, stubRouter, useBillingRouter, useRequirePage, PageContext, createSection, plus the shared APIKeysSection/BillingSection used by both User and Org composed dirs.

Nothing is exported from the package yet.

Changesets live only on the release PR #9144; this PR carries none.


Stack

Reviewed as a stack. The 7 PRs merge bottom-up into release branch #9144, which integrates into main as a single unit.

#PRLayer
#9144release → main (integration)
1#9130moduleManager getter across build boundary
2#9131shared profile UI infra
3#9132extract Section components from profile pages
4#9133 👈composed shell + providers infra
5#9134composed UserProfile
6#9135composed OrganizationProfile
7#9136expose @clerk/ui/experimental (public API)

@changeset-bot

changeset-botBot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f47096

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

@coderabbitai

coderabbitaiBot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f50660d-6bcb-44f4-aa04-f1bd5bd56718

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@vercel

vercelBot commented Jul 10, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 12:05pm
swingsetReadyReadyPreview, CommentJul 16, 2026 12:05pm

Request Review

@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 6ff5709 to cb7fd8aCompareJuly 13, 2026 14:08
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 411027c to 9c65deeCompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from cb7fd8a to 13f68a7CompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 9c65dee to e791dc6CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 13f68a7 to 669a561CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 669a561 to 8e09a06CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from e791dc6 to 5cb97b6CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenter marked this pull request as ready for review July 15, 2026 15:04
}
}

export function useBillingRouter(): { router: RouteContextValue; route: BillingRoute } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this a pattern we're going to need elsewhere? We have the VirtualRouter component already, which could potentially be usable here in some fashion, but that one is sort of geared towards modal routing so might not slot in as cleanly.

Food for thought, don't think we need to adjust now but I expect to see this pattern emerge more than once.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Agreed it'''ll recur. Not reusing VirtualRouter: that'''s BaseRouter matching, but composed is leaf-only (stubRouter throws on /matches). Typed in-memory state is the intended shape here. Added a comment noting why. Extract when a 2nd composed router shows up.

export function useRequirePage(componentName: string): boolean {
const page = useContext(PageContext);
if (!page) {
if (typeof __DEV__ === 'undefined' || __DEV__) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would only throw when the package is being developed, not in Clerk dev mode. I think this is intentional, but calling it out regardless

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Good catch — was dead for consumers (__DEV__ baked false in published @clerk/ui). Now gated on isDevelopmentSDK(clerk), same as createCustomPages: throws in consumer dev, null in prod. Throw tests mark the fixture as a dev SDK.

Comment on lines +4 to +12
const store = new WeakMap<object, EmotionCache>();
// their own. The cache is stored with the nonce/cssLayerName it was built from so a
// later change to either rebuilds it (matching the AIO StyleCacheProvider) instead
// of pinning whatever the first-mounted root happened to see.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure if this is really needed. I would recommend that the comment is trimmed down to more clearly explain why this construct exists, or removed.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Trimmed to 3 lines.

Comment on lines +153 to +154
// Match the portal path's normalization (Components.tsx:209) so a cssLayerName
// nested inside appearance.theme gets hoisted to top-level for @layer wrapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this reference file / line no. will get out of date quickly

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Dropped the file:line ref.

Comment on lines +71 to +73
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Compressed to one line (kept a terse why for the cast) + dropped the duplicate type-def comment above it.

… on nonce/layer
Consolidate the duplicated environment + module-manager reads from the composed
providers into resolveComposedClerkRuntime, dropping the `as any` casts for
narrow structural types and warning once on version skew instead of a silent
blank render. Store the shared emotion cache with the nonce/cssLayerName it was
built from so a later change rebuilds it (matching the AIO StyleCacheProvider).
Collapse the multi-line SAFETY/why blocks in ProfileProviderShell and
styleCacheStore to terse single lines, drop the brittle Components.tsx:209
file/line reference, and note why composed billing uses a typed in-memory
router instead of VirtualRouter/BaseRouter.
__DEV__ is baked to false in the published @clerk/ui, so useRequirePage's
misuse error never reached consumers. Gate it on isDevelopmentSDK(clerk) — the
same helper createCustomPages uses — so it throws in the consumer's dev SDK and
degrades to a null render in production.
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 3e7ace2 to 5f47096CompareJuly 16, 2026 12:02
@alexcarpenter
alexcarpenter merged commit 9c7a1a0 into carp/profile-releaseJul 16, 2026
7 of 10 checks passed
@alexcarpenter
alexcarpenter deleted the carp/profile-stack-04-composed-shell branch July 16, 2026 12:03
Sign up for freeto 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

@alexcarpenter@brkalow
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(ui): add composed profile shell, providers infra and stub router by alexcarpenter · Pull Request #9133 · clerk/javascript · GitHub
Skip to content

feat(ui): add composed profile shell, providers infra and stub router - #9133

Merged
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell
Jul 16, 2026
Merged

feat(ui): add composed profile shell, providers infra and stub router#9133
alexcarpenter merged 4 commits into
carp/profile-releasefrom
carp/profile-stack-04-composed-shell

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stack 4/7. New (unexported) infra for the composed profile API: ProfileProviderShell, stubRouter, useBillingRouter, useRequirePage, PageContext, createSection, plus the shared APIKeysSection/BillingSection used by both User and Org composed dirs.

Nothing is exported from the package yet.

Changesets live only on the release PR #9144; this PR carries none.


Stack

Reviewed as a stack. The 7 PRs merge bottom-up into release branch #9144, which integrates into main as a single unit.

#PRLayer
#9144release → main (integration)
1#9130moduleManager getter across build boundary
2#9131shared profile UI infra
3#9132extract Section components from profile pages
4#9133 👈composed shell + providers infra
5#9134composed UserProfile
6#9135composed OrganizationProfile
7#9136expose @clerk/ui/experimental (public API)

@changeset-bot

changeset-botBot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f47096

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

@coderabbitai

coderabbitaiBot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f50660d-6bcb-44f4-aa04-f1bd5bd56718

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@vercel

vercelBot commented Jul 10, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 12:05pm
swingsetReadyReadyPreview, CommentJul 16, 2026 12:05pm

Request Review

@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 6ff5709 to cb7fd8aCompareJuly 13, 2026 14:08
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 411027c to 9c65deeCompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from cb7fd8a to 13f68a7CompareJuly 13, 2026 14:12
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from 9c65dee to e791dc6CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 13f68a7 to 669a561CompareJuly 13, 2026 14:34
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 669a561 to 8e09a06CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-03-extract-sections branch from e791dc6 to 5cb97b6CompareJuly 13, 2026 19:20
@alexcarpenter
alexcarpenter marked this pull request as ready for review July 15, 2026 15:04
}
}

export function useBillingRouter(): { router: RouteContextValue; route: BillingRoute } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this a pattern we're going to need elsewhere? We have the VirtualRouter component already, which could potentially be usable here in some fashion, but that one is sort of geared towards modal routing so might not slot in as cleanly.

Food for thought, don't think we need to adjust now but I expect to see this pattern emerge more than once.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Agreed it'''ll recur. Not reusing VirtualRouter: that'''s BaseRouter matching, but composed is leaf-only (stubRouter throws on /matches). Typed in-memory state is the intended shape here. Added a comment noting why. Extract when a 2nd composed router shows up.

export function useRequirePage(componentName: string): boolean {
const page = useContext(PageContext);
if (!page) {
if (typeof __DEV__ === 'undefined' || __DEV__) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would only throw when the package is being developed, not in Clerk dev mode. I think this is intentional, but calling it out regardless

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Good catch — was dead for consumers (__DEV__ baked false in published @clerk/ui). Now gated on isDevelopmentSDK(clerk), same as createCustomPages: throws in consumer dev, null in prod. Throw tests mark the fixture as a dev SDK.

Comment on lines +4 to +12
const store = new WeakMap<object, EmotionCache>();
// their own. The cache is stored with the nonce/cssLayerName it was built from so a
// later change to either rebuilds it (matching the AIO StyleCacheProvider) instead
// of pinning whatever the first-mounted root happened to see.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure if this is really needed. I would recommend that the comment is trimmed down to more clearly explain why this construct exists, or removed.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Trimmed to 3 lines.

Comment on lines +153 to +154
// Match the portal path's normalization (Components.tsx:209) so a cssLayerName
// nested inside appearance.theme gets hoisted to top-level for @layer wrapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this reference file / line no. will get out of date quickly

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Dropped the file:line ref.

Comment on lines +71 to +73
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: __internal_environment is a real getter on the clerk-js Clerk class
// but not on the shared LoadedClerk interface. Narrowing to an optional field
// (not `any`) keeps the access typed and forces callers to handle `undefined`.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Compressed to one line (kept a terse why for the cast) + dropped the duplicate type-def comment above it.

… on nonce/layer
Consolidate the duplicated environment + module-manager reads from the composed
providers into resolveComposedClerkRuntime, dropping the `as any` casts for
narrow structural types and warning once on version skew instead of a silent
blank render. Store the shared emotion cache with the nonce/cssLayerName it was
built from so a later change rebuilds it (matching the AIO StyleCacheProvider).
Collapse the multi-line SAFETY/why blocks in ProfileProviderShell and
styleCacheStore to terse single lines, drop the brittle Components.tsx:209
file/line reference, and note why composed billing uses a typed in-memory
router instead of VirtualRouter/BaseRouter.
__DEV__ is baked to false in the published @clerk/ui, so useRequirePage's
misuse error never reached consumers. Gate it on isDevelopmentSDK(clerk) — the
same helper createCustomPages uses — so it throws in the consumer's dev SDK and
degrades to a null render in production.
@alexcarpenter
alexcarpenterforce-pushed the carp/profile-stack-04-composed-shell branch from 3e7ace2 to 5f47096CompareJuly 16, 2026 12:02
@alexcarpenter
alexcarpenter merged commit 9c7a1a0 into carp/profile-releaseJul 16, 2026
7 of 10 checks passed
@alexcarpenter
alexcarpenter deleted the carp/profile-stack-04-composed-shell branch July 16, 2026 12:03
Sign up for freeto 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

@alexcarpenter@brkalow