Skip to content

feat(js): add InviteMembersButton and Clerk.openInviteMembers#9124

Open
alexcarpenter wants to merge 5 commits into
mainfrom
invite-members-button
Open

feat(js): add InviteMembersButton and Clerk.openInviteMembers#9124
alexcarpenter wants to merge 5 commits into
mainfrom
invite-members-button

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Description

Adds <InviteMembersButton /> to @clerk/react (re-exported through @clerk/nextjs), a control component that works like <SignInButton mode="modal">: wrap your own button and clicking it opens a modal containing only the organization invite-members form. It's backed by new stable Clerk.openInviteMembers() / closeInviteMembers() methods on the core Clerk class and a new @clerk/ui InviteMembers modal (its own code-split chunk) that reuses the existing invite form, renders it inside a card matching the sign-in modal, and gates on the org:sys_memberships:manage permission. To test: run pnpm dev:sandbox, sign in with an active organization as an admin, and open "Open invite members" under the Orgs section.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Docs PR: clerk/clerk#2921

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features
    • Added an organization invite-members modal for inviting users and showing an “invitations sent” confirmation.
    • Introduced InviteMembersButton (default or custom children) and added it to the React and Next.js public exports.
    • Added programmatic controls to open/close the invite-members modal, including isomorphic support.
  • Bug Fixes
    • Added support to hide the invite form reset button via hideResetButton.
  • Tests
    • Added coverage for InviteMembersButton, InviteMembersModal, and permission-missing warnings.

Adds a control component that opens the organization invite-members form in a modal, plus openInviteMembers/closeInviteMembers on the Clerk class and an @clerk/ui InviteMembers modal.
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 79c2172

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
Name Type
@clerk/clerk-js Minor
@clerk/shared Minor
@clerk/react Minor
@clerk/nextjs Minor
@clerk/ui Minor
@clerk/chrome-extension Patch
@clerk/electron Patch
@clerk/expo Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/expo-passkeys Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/headless Patch
@clerk/hono Patch
@clerk/localizations Patch
@clerk/msw Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/vue Patch
@clerk/swingset Patch

Not sure what this means? Click here to learn what changesets are.

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

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jul 10, 2026 3:54pm
swingset Ready Ready Preview, Comment Jul 10, 2026 3:54pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an organization invite-members modal with permission-gated UI, public Clerk open/close APIs, React and Next.js launchers, lazy component wiring, form customization, tests, sandbox access, and package changeset documentation.

Changes

Invite members experience

Layer / File(s) Summary
Invite-members contracts and guards
packages/shared/src/types/clerk.ts, packages/shared/src/internal/clerk-js/warnings.ts
Adds modal and button prop types, Clerk API declarations, organization callers, and warning support.
Modal implementation and rendering
packages/ui/src/components/InviteMembers/*, packages/ui/src/Components.tsx, packages/ui/src/lazyModules/components.ts, packages/ui/src/components/OrganizationProfile/InviteMembersForm.tsx
Adds the permission-gated two-step modal, lazy loading and state wiring, form reset-button control, and modal tests.
Public APIs and React launchers
packages/clerk-js/src/core/clerk.ts, packages/react/src/isomorphicClerk.ts, packages/react/src/components/*, packages/nextjs/src/*
Adds programmatic open/close methods, pre-load invocation replay, InviteMembersButton, exports, and component tests.
Sandbox and release integration
packages/clerk-js/sandbox/*, .changeset/invite-members-button.md, packages/ui/bundlewatch.config.json
Adds sandbox access, documents the minor package releases and new APIs, and raises the UI bundle size threshold.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReactClient
  participant IsomorphicClerk
  participant Clerk
  participant Components
  participant InviteMembersModal
  ReactClient->>IsomorphicClerk: click InviteMembersButton
  IsomorphicClerk->>Clerk: openInviteMembers(props)
  Clerk->>Components: openModal('inviteMembers', props)
  Components->>InviteMembersModal: mount modal at /inviteMembers
  InviteMembersModal-->>ReactClient: render invite form
Loading

Poem

I’m a bunny with invites to send,
Through modal doors where memberships bend.
Click, hop, and the form appears,
With permissions guarding the ears.
APIs bloom—what a carrot-filled trend!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main change: adding InviteMembersButton and Clerk.openInviteMembers.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9124

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9124

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9124

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9124

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9124

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9124

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9124

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9124

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9124

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9124

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9124

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9124

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9124

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9124

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9124

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9124

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9124

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9124

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9124

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9124

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9124

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9124

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9124

commit: 79c2172

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-10T15:55:59.616Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 4
🔴 Breaking changes 0
🟡 Non-breaking changes 4
🟢 Additions 10

🤖 This report was reviewed by claude-sonnet-4-6.


@clerk/clerk-js

Current version: 6.25.2
Recommended bump: MINOR → 6.26.0

Subpath .

🟢 Additions (2)

Added: Clerk.closeInviteMembers
+ closeInviteMembers: () => void;

Added property Clerk.closeInviteMembers

Added: Clerk.openInviteMembers
+ openInviteMembers: (props?: InviteMembersModalProps) => void;

Added property Clerk.openInviteMembers

Subpath ./no-rhc

🟢 Additions (2)

Added: Clerk.closeInviteMembers
+ closeInviteMembers: () => void;

Added property Clerk.closeInviteMembers

Added: Clerk.openInviteMembers
+ openInviteMembers: (props?: InviteMembersModalProps) => void;

Added property Clerk.openInviteMembers


@clerk/react

Current version: 6.12.2
Recommended bump: MINOR → 6.13.0

🟢 Additions (1)

Added: InviteMembersButton

+ InviteMembersButton: {
+   (props: import("@clerk/shared/types").Without<WithClerkProp<React.PropsWithChildren<import("@clerk/shared/types").InviteMembersProps>>, "clerk">): React.JSX.Element | null;
+   displayName: string;
+ }

Added variable InviteMembersButton


@clerk/shared

Current version: 4.25.2
Recommended bump: MINOR → 4.26.0

Subpath ./internal/clerk-js/warnings

🟡 Non-breaking Changes (1)

Modified: warnings
// ... 7 unchanged lines elided ...
    cannotRenderSignInComponentWhenSessionExists: string;
    cannotRenderSignInComponentWhenTaskExists: string;
    cannotRenderComponentWhenUserDoesNotExist: string;
-   createCannotRenderComponentWhenOrgDoesNotExist: (componentName: "OrganizationProfile" | "ConfigureSSO") => string;
-   cannotRenderAnyOrganizationComponent: (componentName: "OrganizationProfile" | "OrganizationSwitcher" | "OrganizationList" | "CreateOrganization" | "TaskChooseOrganization" | "ConfigureSSO") => string;
+   createCannotRenderComponentWhenOrgDoesNotExist: (componentName: "OrganizationProfile" | "InviteMembers" | "ConfigureSSO") => string;
+   createCannotRenderComponentWhenPermissionIsMissing: (componentName: "InviteMembers", permission: string) => string;
+   cannotRenderAnyOrganizationComponent: (componentName: "OrganizationProfile" | "InviteMembers" | "OrganizationSwitcher" | "OrganizationList" | "CreateOrganization" | "TaskChooseOrganization" | "ConfigureSSO") => string;
    cannotRenderAnyBillingComponent: (componentName: "PricingTable" | "Checkout" | "PlanDetails") => string;
    cannotOpenUserProfile: string;
    cannotOpenCheckout: string;
// ... 10 unchanged lines elided ...

Static analyzer: Breaking change in variable warnings: Type changed: {proxyUrlAsFunctionNotSupported:string;domainAsFunctionNotSupported:string;cannotRenderComponentWhenSessionExists:strin…{proxyUrlAsFunctionNotSupported:string;domainAsFunctionNotSupported:string;cannotRenderComponentWhenSessionExists:strin…

🤖 AI review (reclassified as non-breaking) (85%): The changes are: (1) createCannotRenderComponentWhenOrgDoesNotExist gains "InviteMembers" as a new union member in its parameter type — widening a function input parameter is non-breaking (consumers calling it with the old set of values still compile); (2) cannotRenderAnyOrganizationComponent similarly gains "InviteMembers" in its parameter union — same reasoning; (3) a new property createCannotRenderComponentWhenPermissionIsMissing is added to the object — adding a new property to an existing object is non-breaking. No existing properties were removed or narrowed.

Subpath ./types

🟡 Non-breaking Changes (2)

Modified: __internal_AttemptToEnableEnvironmentSettingParams
  type __internal_AttemptToEnableEnvironmentSettingParams = {
    for: 'organizations';
-   caller: 'OrganizationSwitcher' | 'OrganizationProfile' | 'OrganizationList' | 'CreateOrganization' | 'TaskChooseOrganization' | 'ConfigureSSO' | 'useOrganizationList' | 'useOrganization';
+   caller: 'OrganizationSwitcher' | 'OrganizationProfile' | 'InviteMembers' | 'OrganizationList' | 'CreateOrganization' | 'TaskChooseOrganization' | 'ConfigureSSO' | 'useOrganizationList' | 'useOrganization';
    onClose?: () => void;
  };

Static analyzer: Breaking change in type alias __internal_AttemptToEnableEnvironmentSettingParams: Type changed: {for:'organizations';caller:'ConfigureSSO'|'CreateOrganization'|'OrganizationList'|'OrganizationProfile'|'OrganizationS…{for:'organizations';caller:'ConfigureSSO'|'CreateOrganization'|'InviteMembers'|'OrganizationList'|'OrganizationProfile…

🤖 AI review (reclassified as non-breaking) (95%): This type is used as a function parameter (input position); adding 'InviteMembers' to the caller union widens the accepted values, which is non-breaking for callers who already pass one of the existing string literals — they remain valid. No existing well-typed consumer code breaks because their current caller values still satisfy the new union.

Modified: __internal_EnableOrganizationsPromptProps
// ... 1 unchanged line elided ...
    onSuccess?: () => void;
    onClose?: () => void;
  } & {
-   caller: 'OrganizationSwitcher' | 'OrganizationProfile' | 'OrganizationList' | 'useOrganizationList' | 'useOrganization';
+   caller: 'OrganizationSwitcher' | 'OrganizationProfile' | 'InviteMembers' | 'OrganizationList' | 'useOrganizationList' | 'useOrganization';
  };

Static analyzer: Breaking change in type alias __internal_EnableOrganizationsPromptProps: Type changed: {caller:'OrganizationList'|'OrganizationProfile'|'OrganizationSwitcher'|'useOrganization'|'useOrganizationList';}&{onSu…{caller:'InviteMembers'|'OrganizationList'|'OrganizationProfile'|'OrganizationSwitcher'|'useOrganization'|'useOrganizat…

🤖 AI review (reclassified as non-breaking) (95%): This type is used as a function parameter (input position via __internal_openEnableOrganizationsPrompt); adding 'InviteMembers' to the caller union widens the accepted set, so all existing callers passing a previously-valid caller value continue to compile without changes.

🟢 Additions (5)

Added: Clerk.closeInviteMembers
+ closeInviteMembers: () => void;

Added property Clerk.closeInviteMembers

Added: Clerk.openInviteMembers
+ openInviteMembers: (props?: InviteMembersModalProps) => void;

Added property Clerk.openInviteMembers

Added: InviteMembersButtonProps
+ type InviteMembersButtonProps = InviteMembersProps;

Added type alias InviteMembersButtonProps

Added: InviteMembersModalProps
+ type InviteMembersModalProps = InviteMembersProps & {
+   getContainer?: () => HTMLElement | null;
+ };

Added type alias InviteMembersModalProps

Added: InviteMembersProps
+ type InviteMembersProps = {
+   appearance?: OrganizationProfileProps['appearance'];
+ };

Added type alias InviteMembersProps


@clerk/ui

Current version: 1.25.2
Recommended bump: MINOR → 1.26.0

Subpath ./internal

🟡 Non-breaking Changes (1)

Modified: ComponentControls
// ... 13 unchanged lines elided ...
      node?: HTMLDivElement;
      props?: unknown;
    }) => void;
-   openModal: <T extends 'googleOneTap' | 'signIn' | 'signUp' | 'userProfile' | 'organizationProfile' | 'createOrganization' | 'userVerification' | 'waitlist' | 'blankCaptcha' | 'enableOrganizationsPrompt'>(modal: T, props: T extends 'signIn' ? SignInProps : T extends 'signUp' ? SignUpProps : T extends 'userVerification' ? __internal_UserVerificationProps : T extends 'waitlist' ? WaitlistProps : T extends 'enableOrganizationsPrompt' ? __internal_EnableOrganizationsPromptProps : UserProfileProps) => void;
-   closeModal: (modal: 'googleOneTap' | 'signIn' | 'signUp' | 'userProfile' | 'organizationProfile' | 'createOrganization' | 'userVerification' | 'waitlist' | 'blankCaptcha' | 'enableOrganizationsPrompt', options?: {
+   openModal: <T extends 'googleOneTap' | 'signIn' | 'signUp' | 'userProfile' | 'organizationProfile' | 'inviteMembers' | 'createOrganization' | 'userVerification' | 'waitlist' | 'blankCaptcha' | 'enableOrganizationsPrompt'>(modal: T, props: T extends 'signIn' ? SignInProps : T extends 'signUp' ? SignUpProps : T extends 'userVerification' ? __internal_UserVerificationProps : T extends 'waitlist' ? WaitlistProps : T extends 'inviteMembers' ? InviteMembersModalProps : T extends 'enableOrganizationsPrompt' ? __internal_EnableOrganizationsPromptProps : UserProfileProps) => void;
+   closeModal: (modal: 'googleOneTap' | 'signIn' | 'signUp' | 'userProfile' | 'organizationProfile' | 'inviteMembers' | 'createOrganization' | 'userVerification' | 'waitlist' | 'blankCaptcha' | 'enableOrganizationsPrompt', options?: {
      notify?: boolean;
    }) => void;
    openDrawer: <T extends 'checkout' | 'planDetails' | 'subscriptionDetails'>(drawer: T, props: T extends 'checkout' ? __internal_CheckoutProps : T extends 'planDetails' ? __internal_PlanDetailsProps : T extends 'subscriptionDetails' ? __internal_SubscriptionDetailsProps : never) => void;
// ... 6 unchanged lines elided ...

Static analyzer: Breaking change in type alias ComponentControls: Type changed: {mountComponent:(params:{appearanceKey:!Uncapitalize:type<import("@clerk/ui").~AppearanceCascade['appearanceKey']>;name…{mountComponent:(params:{appearanceKey:!Uncapitalize:type<import("@clerk/ui").~AppearanceCascade['appearanceKey']>;name…

🤖 AI review (reclassified as non-breaking) (85%): The change adds a new 'inviteMembers' variant to the discriminated union in openModal and closeModal. For openModal, this widens the generic constraint (non-breaking for callers) and adds a new conditional branch for InviteMembersModalProps; existing callers passing the old set of modal types are unaffected. For closeModal, the union in the modal parameter is widened (accepting more values is non-breaking for callers). No existing variant was removed or renamed.


Report generated by Break Check

Last ran on 79c2172.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
packages/react/src/isomorphicClerk.ts (1)

1073-1087: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add explicit return types to the new public methods.

openInviteMembers and closeInviteMembers are public APIs and should explicitly return void.

Proposed fix
-  openInviteMembers = (props?: InviteMembersModalProps) => {
+  openInviteMembers = (props?: InviteMembersModalProps): void => {
...
-  closeInviteMembers = () => {
+  closeInviteMembers = (): void => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react/src/isomorphicClerk.ts` around lines 1073 - 1087, Add explicit
void return types to the public openInviteMembers and closeInviteMembers
methods, preserving their existing behavior and parameter typing.

Sources: Coding guidelines, Learnings

packages/react/src/components/index.ts (1)

38-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Verify this feature-level barrel export.

This components/index.ts re-export conflicts with the repository guidance to avoid barrel files because of circular-dependency risk. Prefer a direct package-entrypoint export, or confirm that this file is intentionally the public entrypoint.

As per coding guidelines, avoid index.ts re-export barrels where possible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react/src/components/index.ts` at line 38, Replace the
InviteMembersButton re-export in the components/index.ts barrel with a direct
package-entrypoint export, or remove it if the barrel is not intended as a
public API; verify consumers import InviteMembersButton through the package
entrypoint and avoid introducing circular dependencies.

Source: Coding guidelines

packages/ui/src/components/OrganizationProfile/InviteMembersForm.tsx (1)

36-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add or confirm regression coverage for the hidden-reset variant.

This changes both the rendered controls and their spacing. Ensure modal tests cover hideResetButton={true} and the default false path.

As per coding guidelines, unit tests are required for new functionality.

Also applies to: 308-331

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/OrganizationProfile/InviteMembersForm.tsx` around
lines 36 - 40, The InviteMembersForm tests lack regression coverage for the
hideResetButton variants. Add or update modal tests covering both
hideResetButton={true} and the default false behavior, asserting the reset
button visibility and controls-row spacing for each path, using the relevant
InviteMembersForm render and controls selectors.

Source: Coding guidelines

packages/react/src/components/__tests__/InviteMembersButton.test.tsx (1)

99-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer a semantic query for the rendered button.

Use screen.getByRole('button', { name: 'Invite' }) instead of container.querySelector('button') so the test verifies the accessible element and remains resilient to DOM structure changes.

As per coding guidelines, React Testing Library tests should use proper test queries.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react/src/components/__tests__/InviteMembersButton.test.tsx` around
lines 99 - 107, The test “does not pass appearance prop to child element” uses a
DOM selector instead of a semantic query. Replace
container.querySelector('button') with screen.getByRole('button', { name:
'Invite' }) and ensure the screen utility is imported from React Testing
Library, while keeping the appearance attribute assertion unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react/src/components/__tests__/InviteMembersButton.test.tsx`:
- Around line 11-17: Replace the untyped any usages in the mockClerk object and
the withClerk mock’s Component and props parameters with appropriate
Clerk/component prop types, or use unknown and narrow it before access. Preserve
compile-time validation of the public component contract while keeping the test
mock behavior unchanged.

In `@packages/react/src/isomorphicClerk.ts`:
- Around line 1073-1078: Update openInviteMembers in the unloaded branch to
preserve an omitted props argument as undefined rather than converting it to the
null cancellation sentinel; assign props directly to preopenInviteMembers so
preload calls without props are replayed correctly.

In `@packages/ui/src/components/InviteMembers/InviteMembers.tsx`:
- Around line 55-62: Prevent unauthorized users from seeing a blank Invite
Members modal: update the `Protect` usage around `InviteMembersModalInner` to
render an access-denied fallback or close the modal when
`org:sys_memberships:manage` is missing, rather than leaving the outer modal
mounted empty. Extend the negative authorization test to assert the chosen
denied or closed outcome.

---

Nitpick comments:
In `@packages/react/src/components/__tests__/InviteMembersButton.test.tsx`:
- Around line 99-107: The test “does not pass appearance prop to child element”
uses a DOM selector instead of a semantic query. Replace
container.querySelector('button') with screen.getByRole('button', { name:
'Invite' }) and ensure the screen utility is imported from React Testing
Library, while keeping the appearance attribute assertion unchanged.

In `@packages/react/src/components/index.ts`:
- Line 38: Replace the InviteMembersButton re-export in the components/index.ts
barrel with a direct package-entrypoint export, or remove it if the barrel is
not intended as a public API; verify consumers import InviteMembersButton
through the package entrypoint and avoid introducing circular dependencies.

In `@packages/react/src/isomorphicClerk.ts`:
- Around line 1073-1087: Add explicit void return types to the public
openInviteMembers and closeInviteMembers methods, preserving their existing
behavior and parameter typing.

In `@packages/ui/src/components/OrganizationProfile/InviteMembersForm.tsx`:
- Around line 36-40: The InviteMembersForm tests lack regression coverage for
the hideResetButton variants. Add or update modal tests covering both
hideResetButton={true} and the default false behavior, asserting the reset
button visibility and controls-row spacing for each path, using the relevant
InviteMembersForm render and controls selectors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: d6df0d3b-58da-450b-8b55-76a78ac44e66

📥 Commits

Reviewing files that changed from the base of the PR and between 145e4d3 and 87fe150.

⛔ Files ignored due to path filters (2)
  • packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap is excluded by !**/*.snap
  • packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (19)
  • .changeset/invite-members-button.md
  • packages/clerk-js/sandbox/app.ts
  • packages/clerk-js/sandbox/template.html
  • packages/clerk-js/src/core/clerk.ts
  • packages/nextjs/src/client-boundary/uiComponents.tsx
  • packages/nextjs/src/index.ts
  • packages/react/src/components/InviteMembersButton.tsx
  • packages/react/src/components/__tests__/InviteMembersButton.test.tsx
  • packages/react/src/components/index.ts
  • packages/react/src/isomorphicClerk.ts
  • packages/react/src/utils/childrenUtils.tsx
  • packages/shared/src/internal/clerk-js/warnings.ts
  • packages/shared/src/types/clerk.ts
  • packages/ui/src/Components.tsx
  • packages/ui/src/components/InviteMembers/InviteMembers.tsx
  • packages/ui/src/components/InviteMembers/__tests__/InviteMembersModal.test.tsx
  • packages/ui/src/components/InviteMembers/index.tsx
  • packages/ui/src/components/OrganizationProfile/InviteMembersForm.tsx
  • packages/ui/src/lazyModules/components.ts

Comment on lines +11 to +17
const mockClerk = {
openInviteMembers: mockOpenInviteMembers,
} as any;

vi.mock('../withClerk', () => {
return {
withClerk: (Component: any) => (props: any) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove untyped any from the test mocks.

The mocked Clerk object, component, and props use any, disabling compile-time validation of the new public component contract. Use typed mocks or unknown with narrowing.

As per coding guidelines, any is disallowed without justification.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react/src/components/__tests__/InviteMembersButton.test.tsx` around
lines 11 - 17, Replace the untyped any usages in the mockClerk object and the
withClerk mock’s Component and props parameters with appropriate Clerk/component
prop types, or use unknown and narrow it before access. Preserve compile-time
validation of the public component contract while keeping the test mock behavior
unchanged.

Source: Coding guidelines

Comment thread packages/react/src/isomorphicClerk.ts
Comment on lines +55 to +62
<Route path='inviteMembers'>
<SubscriberTypeContext.Provider value='organization'>
<Protect permission='org:sys_memberships:manage'>
{/*TODO: Used by InvisibleRootBox, can we simplify? */}
<div>
<InviteMembersModalInner />
</div>
</Protect>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Avoid opening an empty modal for unauthorized users.

Protect returns null here, but the outer modal is already mounted. A user without org:sys_memberships:manage gets a blank dialog/overlay. Render an access-denied fallback or close the modal when authorization fails, and extend the negative test to assert that outcome.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/InviteMembers/InviteMembers.tsx` around lines 55 -
62, Prevent unauthorized users from seeing a blank Invite Members modal: update
the `Protect` usage around `InviteMembersModalInner` to render an access-denied
fallback or close the modal when `org:sys_memberships:manage` is missing, rather
than leaving the outer modal mounted empty. Extend the negative authorization
test to assert the chosen denied or closed outcome.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/shared/src/internal/clerk-js/warnings.ts (1)

30-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the warning factory.

This helper is exposed through the exported warnings object. Add : string to satisfy the repository’s TypeScript return-type guideline.

Suggested change
-const createCannotRenderComponentWhenPermissionIsMissing = (componentName: 'InviteMembers', permission: string) => {
+const createCannotRenderComponentWhenPermissionIsMissing = (
+  componentName: 'InviteMembers',
+  permission: string,
+): string => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/src/internal/clerk-js/warnings.ts` around lines 30 - 34, Add
the explicit return type annotation `: string` to the
`createCannotRenderComponentWhenPermissionIsMissing` warning factory, which
returns the result of `formatWarning` and is exposed through the exported
`warnings` object.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/shared/src/internal/clerk-js/warnings.ts`:
- Around line 30-34: Update createCannotRenderComponentWhenPermissionIsMissing
to say “this is a no-op” instead of “this is no-op,” and update the
corresponding expected warning text in the warnings test assertion.

---

Nitpick comments:
In `@packages/shared/src/internal/clerk-js/warnings.ts`:
- Around line 30-34: Add the explicit return type annotation `: string` to the
`createCannotRenderComponentWhenPermissionIsMissing` warning factory, which
returns the result of `formatWarning` and is exposed through the exported
`warnings` object.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: f0cbadb3-a7d3-46fa-a74c-b6758693d6d2

📥 Commits

Reviewing files that changed from the base of the PR and between 11c1341 and 79c2172.

📒 Files selected for processing (6)
  • .changeset/invite-members-button.md
  • packages/clerk-js/src/core/clerk.ts
  • packages/react/src/components/InviteMembersButton.tsx
  • packages/react/src/isomorphicClerk.ts
  • packages/shared/src/internal/clerk-js/__tests__/warnings.test.ts
  • packages/shared/src/internal/clerk-js/warnings.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/invite-members-button.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/clerk-js/src/core/clerk.ts
  • packages/react/src/components/InviteMembersButton.tsx
  • packages/react/src/isomorphicClerk.ts

Comment on lines +30 to +34
const createCannotRenderComponentWhenPermissionIsMissing = (componentName: 'InviteMembers', permission: string) => {
return formatWarning(
`<${componentName}/> cannot render unless the current user has the \`${permission}\` permission. Since the current user is missing this permission, this is no-op. Render it only for members who can manage memberships, for example by wrapping it in <Show when={{ permission: '${permission}' }}>.`,
);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the no-op wording in the development warning.

Line 32 says this is no-op; change it to this is a no-op. Update the corresponding test assertion in packages/shared/src/internal/clerk-js/__tests__/warnings.test.ts as part of the same change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/src/internal/clerk-js/warnings.ts` around lines 30 - 34,
Update createCannotRenderComponentWhenPermissionIsMissing to say “this is a
no-op” instead of “this is no-op,” and update the corresponding expected warning
text in the warnings test assertion.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant