Components / Unstyled React primitives
Radix Primitives
Use focused UI building blocks when you need control over markup and appearance.
Understand the resource
What it is.
Radix Primitives is a React library for common interaction patterns such as dialogs, menus, tabs and tooltips. Its component parts handle state and many keyboard details while leaving styling to the application. This review covers Primitives rather than the separate Radix Themes offering.
When it is useful
It fits teams building a shared React component layer or improving a custom interface one control at a time. It also helps explain the behavior beneath many styled wrappers already in the catalog.
A proposed application
Put it to work.
This example illustrates a possible use. The proposed workflow and expected result are separate from the bounded checks recorded below.
Try this
Proposed workflow: replace three different edit-record modals with one Radix Dialog wrapper. Include a title and description, make the first field labelled, and restore focus to the row's Edit button on cancellation. Add a second review step for unsaved changes instead of assuming Escape should discard work. Our fixture tested dialog mechanics, not database updates or that unsaved-change policy.
Result to aim for
A consistent editing shell that can be styled for the product while keeping data validation and persistence explicit.
Before you start
What you need.
- A compatible React application. The pinned @radix-ui/react-dialog 1.3.1 manifest accepts React 16.8 through 19; the fixture used React 19.3.1.
- Styles, useful accessible names and careful forwarding of props and refs when composing custom children. Primitives is MIT licensed.
The practical boundaries
What to watch for.
- The asChild composition option can lose behavior when a custom child drops props or refs, or uses an unsuitable element.
- Keep separately installed Radix packages aligned; shared dependency duplication can complicate upgrades and bundle size.
- A modal can constrain interaction while open; it cannot establish who may read or change a record.
Review record
What we could establish.
- Why included (2026-10-19): Adds the widely used primitive layer behind many React kits, so readers can compare that foundation with finished styling systems. GitHub reported 19,406 stars for the upstream repository (radix-ui/primitives); the sampled default-branch commit is dated 2026-10-14. This is a dated popularity/activity snapshot, not a measured growth rate or safety ranking.
- Public official documentation and declared package requirements reviewed. The example above is a proposed workflow; executed checks are listed separately below.
- Earlier selector, focus-timing and setup probes remain in the retained evidence. Final conclusions use the corrected bounded tests and keep unmet coverage explicit.
The source-review notes describe documentation and identity. The account-free evaluation below records exactly which public interaction or local fixture was checked, what happened, and what remains untested.
Check current access, terms, licensing and data handling before using a resource with your own material.
Runtime and security evaluation
What the evaluation covers.
The official edit-profile dialog passed opening, six Tab steps and Escape focus restoration. The pinned Dialog 1.3.1 fixture passed six additional checks, including state callbacks and React text insertion. Save changes was never activated.
Methods: Public HTTP response · Public browser sample · Local package fixture. A completed check covers the stated sample; it is not a safety certification.
- Target
- https://www.radix-ui.com/primitives/docs/components/dialog plus a local @radix-ui/react-dialog fixture
- Version or capture
- Local @radix-ui/react-dialog 1.3.1; React and React DOM 19.3.1 for the Dialog fixtures. Public demo is separately unversioned.
- Integrity scope
- Package archive SHA-256 485336a3a4118d0f7e49504a3b2752c8975ea0e5e3ad1c944ff055d2d2fc15f2 (npm SHA-512 integrity matched). Public document SHA-256 6b061c025b95533dfa1b6ec88666c7471e4a3a6d2e3f38be70e70a004be7572e; response-body scope only.
Test conditions
- Chrome 155.0.8059.82, 1280 x 900 desktop viewport; UTC browser context.
- Fresh temporary browser profile and context; no inherited account storage. Child environment allowed only runtime paths, with credential variables omitted.
- Public HTTPS GET/HEAD only; form submission, non-read methods, WebSockets, service workers and downloads blocked. Selected telemetry requests were blocked.
- Exact direct dependencies; 38-package lock installed with --ignore-scripts, empty user/global npm configs, isolated cache and allowlisted environment. No lifecycle install scripts ran.
- Local browser responses served only the authored fixture, pinned bundle or stylesheet. External HTTP(S) requests were denied, with zero recorded attempts in the final fixture. WebSocket attempts were blocked but not counted.
- Existing Rolldown 1.2.14 bundled inspected prebuilt exports without running upstream build scripts.
Individual checks and observations
Public sample response
passPublic HTTP response: Anonymous public GET; capture status, response-body hash and selected headers.
Observed: HTTP 200; https://www.radix-ui.com/primitives/docs/components/dialog. Reachability only.
Keyboard open and dialog semantics
passPublic browser sample: Focus the first documented dialog trigger; press Enter; inspect visible dialog and accessible-name reference.
Observed: A visible dialog opened; aria-labelledby target present=True. Presence of a reference is not a screen-reader test.
Bounded dialog focus containment
passPublic browser sample: Press Tab six times while the sample remains open and inspect focus containment after each press.
Observed: All six settled Tab samples stayed inside the dialog. This does not establish every focus path or nested-overlay behavior.
Escape and focus restoration
passPublic browser sample: Press Escape, wait for exit animation and inspect closed state plus focus on original trigger.
Observed: Closed=true; trigger focus restored=true. No save action or form submission.
Pinned dialog opening and labels
passLocal package fixture: Press Enter on the authored trigger and inspect the dialog name and description relationships.
Observed: {"title":"Synthetic settings","description":"A synthetic local form; nothing is saved."}
Initial focus
passLocal package fixture: Inspect focused element after the modal opens.
Observed: {"inside":true,"tag":"INPUT"}
Tab containment
passLocal package fixture: Press Tab six times with a brief focus-settlement wait; inspect containment.
Observed: [true,true,true,true,true,true]
React text insertion boundary
passLocal package fixture: Render a synthetic HTML-looking string as a React child and inspect text, image count and recorded HTTP(S) request attempts.
Observed: Literal text retained=true; image nodes=0; recorded HTTP(S) request attempts=0. React text composition only; no HTML sanitizer tested. WebSocket attempts were blocked but not counted.
Escape closure and restoration
passLocal package fixture: Press Escape and inspect removed dialog plus focus on original trigger.
Observed: {"closed":true,"focused":true}
Close action and state callback
passLocal package fixture: Reopen by pointer, activate the component Close control and inspect open-change callbacks.
Observed: Open state callbacks [true,false,true,false]; no persistence handler attached.
Security findings and boundaries
Passive inspection of official public response headers and request hosts, declared package metadata, and the exact local fixture where stated. No exploitation, authentication, private input or backend writes.
Observed · info
Dialog state stayed local in the fixture
The synthetic open/close callback sequence was true, false, true, false and zero HTTP(S) request attempts were recorded. WebSocket attempts were blocked but not counted. That describes the authored fixture; production handlers can add network and persistence behavior.
Observed · info
Sampled response and request surface
The anonymous HTTPS demo response included strict-transport-security. Requests attempted 2 additional host(s), including static.cloudflareinsights.com, www.googletagmanager.com. The guard blocked 2 request(s). Host attempts include blocked requests; this does not establish retained data or exploit resistance.
Observed · info
Pinned fixture network boundary
All six @radix-ui/react-dialog fixture checks completed with zero recorded HTTP(S) request attempts and zero page errors. WebSocket attempts were blocked but not counted. This applies to the small authored fixture; it is not a claim about every export, dependency or integration.
Practical caveats
- The asChild composition option can lose behavior when a custom child drops props or refs, or uses an unsuitable element.
- Keep separately installed Radix packages aligned; shared dependency duplication can complicate upgrades and bundle size.
- A modal can constrain interaction while open; it cannot establish who may read or change a record.
What remains untested
- Only Dialog was installed. No whole-library, nested overlay, router, hydration or mobile assistive-technology test was performed.
- The HTML-looking string stayed literal through React child rendering; dangerouslySetInnerHTML and HTML sanitizers were not tested.
Security limits
- No authentication, authorization, tenant isolation, retention or backend processing was tested.
- No comprehensive source/dependency audit, vulnerability scan or security certification. Header presence or absence describes one response, not the safety of a product.
- Request guards can alter normal page behavior. Full browser, mobile, assistive-technology and performance coverage is outside this evaluation.
Evaluation sources
- Official documentation
- Official evaluated sample
- Upstream repository
- GitHub repository snapshot endpoint
Star count and repository identity observed 2026-10-19.
- Sampled source revision
Default branch snapshot; not a claim that the hosted demo deploys this exact commit.
- Package manifest snapshot
Exact registry version and dependency/license declaration; installed only where the evaluation says so.
- Radix accessibility guidance
- Composition and custom-child requirements
Follow the evidence
Sources and further reading.
- Official documentation
- Official evaluated sample
- Upstream repository
- GitHub repository snapshot endpoint
Star count and repository identity observed 2026-10-19.
- Sampled source revision
Default branch snapshot; not a claim that the hosted demo deploys this exact commit.
- Package manifest snapshot
Exact registry version and dependency/license declaration; installed only where the evaluation says so.
- Radix accessibility guidance
- Composition and custom-child requirements
Keep exploring