Skip to content

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

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

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

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jun 10, 2026

Copy link
Copy Markdown
Member

Summary

  • '' (empty string) is falsy in React 19's boolean attribute handler and does not set the inert attribute, leaving hidden panel/collapsible content focusable and interactive
  • Switches all inert usages to 'true' (truthy in both React 18 and 19) so the attribute is always present when needed
  • Adds a declare module 'react' type augmentation to packages/ui/src/global.d.ts and a new packages/headless/src/global.d.ts, replacing three ad-hoc @ts-ignore / as any suppressions

How 'true' works in both versions

React 18 React 19
inert handling Unknown attribute → setAttribute('inert', 'true') Boolean prop → node.inert = 'true'Boolean('true') === true
DOM result inert="true" inert=""
Browser inerts element

Tests assert toHaveAttribute('inert') (presence only) since the rendered value intentionally differs between versions.

Test plan

  • pnpm --filter @clerk/ui test — all 27 Collapsible tests pass, inert tests updated to presence-only assertion
  • pnpm --filter @clerk/headless test — new "non-selected panels have inert attribute" and "inert updates when selection changes" tests pass

Summary by CodeRabbit

  • Bug Fixes
    • Fixed React 19 compatibility issue with interactive hidden panels and collapsible content. When UI elements are hidden or collapsed, they are now properly marked as non-interactive, preventing unintended user interaction. This ensures hidden content remains inaccessible to user input and improves overall accessibility.

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.
@changeset-bot

changeset-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 91077cd

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

This PR includes changesets to release 3 packages
Name Type
@clerk/ui Patch
@clerk/chrome-extension 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 Jun 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
swingset Ready Ready Preview, Comment Jun 10, 2026 11:32pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
clerk-js-sandbox Skipped Skipped Jun 10, 2026 11:32pm

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

📝 Walkthrough

Walkthrough

This PR fixes React 19 compatibility for the HTML inert attribute by standardizing its value to 'true' instead of empty strings. Type definitions are augmented in both headless and ui packages, components are updated to use the correct value, TypeScript suppression comments are removed, and tests validate the corrected behavior across React 18/19.

Changes

React 19 inert attribute compatibility

Layer / File(s) Summary
Type augmentations for inert attribute
packages/headless/src/global.d.ts, packages/ui/src/global.d.ts
React module augmentations extend HTMLAttributes with optional inert?: 'true' | undefined typing in both the headless and ui packages, establishing a consistent type contract for the attribute.
Component inert attribute updates
packages/ui/src/elements/Collapsible.tsx, packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx, packages/ui/src/components/PricingTable/PricingTableMatrix.tsx
Collapsible, KeylessPrompt, and PricingTableMatrix set inert to 'true' when content is hidden, replacing empty-string workarounds. TypeScript suppression comments are removed from components now that types are properly augmented.
Test coverage for inert behavior
packages/ui/src/elements/__tests__/Collapsible.test.tsx, packages/headless/src/primitives/tabs/tabs.test.tsx
Tests in Collapsible and Tabs verify correct inert attribute presence on hidden panels and confirm the attribute updates when visibility state changes, accounting for React 18/19 normalization.
Changeset documentation
.changeset/fix-inert-react-19-compat.md
Patch release note for @clerk/ui documents the inert attribute fix resolving React 19 interactivity issues in hidden panels and collapsible content.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

A rabbit hops through type definitions tall,
Setting inert to 'true' once and for all,
React 19 dances without suppression cries—
Hidden panels stay still while tests verify,
No empty strings, just clarity in every try! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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
Title check ✅ Passed The title accurately and concisely summarizes the main change: fixing the inert attribute for React 18/19 compatibility, which is the core objective of this PR.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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-10T23:33:33.381Z

Summary

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

Note
Break Check could not snapshot 3 subpaths; the diff below excludes them.

  • @clerk/astro ./env: Internal Error: Unable to determine module for: /home/runner/_work/javascript/javascript/packages/astro/env.d.ts You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/shared ./cookie: Internal Error: Unable to follow symbol for "Cookies" You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/testing ./cypress: Symbol not found for identifier: Cypress

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 91077cd.

@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: 91077cd

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.
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