Skip to content

fix(ui): inert attribute for React 18/19 compatibility#8820

Open
alexcarpenter wants to merge 6 commits into
mainfrom
fix/inert-react-19-compat
Open

fix(ui): inert attribute for React 18/19 compatibility#8820
alexcarpenter wants to merge 6 commits into
mainfrom
fix/inert-react-19-compat

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jun 10, 2026

Copy link
Copy Markdown
Member

Summary

  • React 19 made inert a real boolean attr → falsy '' no longer sets it → hidden panel/collapsible/keyless content stayed interactive when rendered in host app
  • Fix: inertProps(active) helper picks the value each React major reflects (18 → '' string, 19 → true boolean; both serialize to inert="")
  • Version check (parseInt(version)) read once at module scope from the consumer's peer react; covers 0.0.0-experimental-* (major 0 → modern)
  • Untyped Record<string, unknown> spread → no @ts-ignore/as any, no react type augmentation
  • Single helper at @clerk/shared/inert, consumed by @clerk/ui + @clerk/headless (no duplication)

Why this subpath, not /dom or /react

  • @clerk/shared/dom is framework-agnostic + react is an optional peer there → importing react into that barrel forces it on non-react dom consumers
  • @clerk/shared/react is a heavy barrel (hooks, contexts, query client) + shared has no sideEffects: false → wouldn't tree-shake, lean headless pulls the whole graph
  • Dedicated top-level subpath: tiny, tree-shakeable, opt-in for react, matches @clerk/shared/webauthn / /color convention

Notes

  • @clerk/headless gains its first internal dep (@clerk/shared); externalized in its vite build (matches @floating-ui/react)
  • New public subpath → api-changes workflow will flag it (additive)

Test plan

  • pnpm --filter @clerk/ui test — Collapsible inert tests (presence-only assertion)
  • pnpm --filter @clerk/headless test — tabs panel inert tests
  • pnpm --filter @clerk/shared test

Summary by CodeRabbit

  • Bug Fixes

    • Ensured the inert attribute behaves correctly across React 18 and React 19 so non-interactive content remains properly disabled.
  • Tests

    • Added/updated tests covering inert behavior for tabs and collapsible components, including post-interaction updates.
  • Chores

    • Added a shared helper for cross-version inert handling, updated package build config and dependencies, and published patch release notes.

@changeset-bot

changeset-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a0d17a5

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

This PR includes changesets to release 22 packages
Name Type
@clerk/ui Patch
@clerk/shared Patch
@clerk/astro Patch
@clerk/chrome-extension Patch
@clerk/react Patch
@clerk/swingset Patch
@clerk/vue Patch
@clerk/backend Patch
@clerk/clerk-js Patch
@clerk/expo-passkeys Patch
@clerk/expo Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/headless Patch
@clerk/hono Patch
@clerk/localizations Patch
@clerk/msw Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/testing 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 Jun 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 Jun 12, 2026 3:27pm
swingset Ready Ready Preview, Comment Jun 12, 2026 3:27pm

Request Review

@github-actions github-actions Bot added the ui label Jun 10, 2026
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request adds a shared inertProps helper that detects the React major version and emits appropriate inert props, migrates several UI components to use it, updates build/dependency config, adds tests, and records changes in changesets.

Changes

React 19 inert attribute compatibility

Layer / File(s) Summary
inertProps utility implementation
packages/shared/src/inert.ts
inertProps(active: boolean) detects React major at module load and returns {} when inactive, { inert: true } for React 19+/modern, or { inert: '' } for older React to match DOM serialization.
Shared package changes & headless build config
.changeset/shared-inert-props-helper.md, .changeset/shared-inert-props-helper.md, packages/headless/package.json, packages/headless/vite.config.ts
Adds a changeset for @clerk/shared documenting the helper, adds @clerk/shared as a headless dependency (workspace:^), and marks @clerk/shared (and subpaths) as external in the headless Vite/Rollup build config.
Component migrations to inertProps
packages/headless/src/primitives/tabs/tabs-panel.tsx, packages/ui/src/elements/Collapsible.tsx, packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx, packages/ui/src/components/PricingTable/PricingTableMatrix.tsx
TabsPanel, Collapsible, KeylessPrompt, and PricingTableMatrix replace inline inert logic and TypeScript suppressions with ...inertProps(...) spreads to delegate version-aware serialization.
Test coverage for inert behavior
packages/headless/src/primitives/tabs/tabs.test.tsx, packages/ui/src/elements/__tests__/Collapsible.test.tsx
Adds presence-only assertions and an async Tabs test to verify inert is applied/updated on visibility changes while tolerating React 18 vs React 19 serialization differences.
UI package React 19 compatibility changeset
.changeset/fix-inert-react-19-compat.md
Adds a @clerk/ui patch changeset noting that inert content is non-interactive across React 18 and React 19.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Suggested reviewers

  • Ephem
  • jacekradko

🐰 I sniffed the code and found a thread,
A tiny helper to mend what was spread,
Eighteen or nineteen, the DOM now agrees,
Inert props hop home with elegant ease,
Hooray for small helpers and fewer confusions!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and concisely describes the main fix: introducing inert attribute support for React 18/19 compatibility across the UI components.
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.

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


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@alexcarpenter alexcarpenter changed the title fix(ui): use 'true' for inert attribute for React 18/19 compatibility fix(ui): inert attribute for React 18/19 compatibility Jun 10, 2026
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-12T15:28:43.002Z

Summary

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

Note
Break Check could not snapshot 1 subpath; the diff below excludes them.

  • @clerk/astro ./env: ambient declaration file (no top-level import or export): API Extractor can only analyze module entry points, so this global-augmentation surface cannot be snapshotted; add the subpath to ignoreSubpaths to acknowledge it (API Extractor: Unable to determine module for: /home/runner/_work/javascript/javascript/packages/astro/env.d.ts)

@clerk/shared

Current version: 4.17.1
Recommended bump: MINOR → 4.18.0

Subpath ./inert

🟢 Additions (1)

Added: ./inert

New subpath export ./inert (1 exported member)


Report generated by Break Check

Last ran on a0d17a5.

@pkg-pr-new

pkg-pr-new Bot commented Jun 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: a0d17a5

Empty string '' is falsy in React 19's boolean attribute handler and
does not set the inert attribute, leaving hidden content interactive.
'true' is truthy in both React 18 and 19.

Also adds global.d.ts type augmentation to packages/headless and
packages/ui, removes per-site @ts-ignore suppressions, and adds
ESLint no-unknown-property ignore for inert.
inert is already a recognized DOM property in eslint-plugin-react, and no usage is on a literal DOM element, so the ignore entry had no effect.
Replace the duplicated per-package inert.ts utilities in @clerk/ui and
@clerk/headless with a single canonical helper at @clerk/shared/inert.
Adds @clerk/shared as @clerk/headless's first internal dependency and
externalizes it in the headless vite build.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant