React
This is the Radix UI Primitives monorepo — a comprehensive library of unstyled, accessible React UI components published as individual packages under the @radix-ui scope. It provides low-level building blocks (primitives) like Dialog, Menu, Popover, Tooltip, Select, Accordion, Tabs, Slider, and more, designed to be composed and styled by consumers. The monorepo also includes an umbrella `radix-ui` aggregate package that re-exports all components.
Tech Stack
Key Features
- Dialog & Alert Dialog
- Menu System
- Popup Components
- Overlay Infrastructure
- Form Components
- Layout & Navigation
- React Foundation Primitives
- Aggregate Package
- Cypress E2E Testing
- SSR Smoke Testing
Entry Points
Start here when working with this codebase
internal/builder/radix-build.jsCLI entry point that triggers the build orchestration for all Radix UI component packages
Start here when debugging build issues, modifying the build process, or understanding how packages are compiled
internal/builder/builder.jsCore build logic that handles compilation, bundling, and output for each package
Start here when you need to change how packages are bundled, add new build targets, or modify compilation settings
scripts/setup-tests.tsInitializes Vitest environment with matchers and global polyfills for all unit tests
Start here when adding new test utilities, custom matchers, or global test configuration
cypress.config.tsConfigures Cypress e2e testing framework with project-specific settings
Start here when adding new Cypress plugins, changing test viewport, or modifying e2e test infrastructure
cypress/support/e2e.jsGlobal test configuration and custom command registration for all Cypress tests
Start here when adding custom Cypress commands or global test hooks
apps/ssr-testing/app/layout.tsxRoot layout for the Next.js SSR testing app providing navigation and page structure
Start here when adding a new component's SSR smoke test page or modifying SSR test navigation
apps/ssr-testing/next.config.jsNext.js build and runtime configuration for the SSR testing application
Start here when SSR tests need new Next.js features or transpilation settings
apps/storybook/eslint.config.jsESLint configuration for the Storybook application combining shared and plugin-specific rules
Start here when adding Storybook-specific linting rules
eslint.config.mjsRoot ESLint configuration for the entire monorepo
Start here when changing linting rules that apply across all packages
internal/eslint-config/index.jsCentralized reusable ESLint configuration imported by all packages in the monorepo
Start here when modifying shared linting rules that propagate to all packages
types/global.d.tsGlobal TypeScript type definitions and module augmentation used across the monorepo
Start here when adding global type declarations or augmenting existing module types
How to Make Changes
Common modification patterns for this codebase
Add a new React component package
packages/react/{component-name}/src/{component-name}.tsxCreate the main component source file with the component implementation. Export the component and its props type. Use React.forwardRef for the root element.
Pattern: Follow the structure in any existing component package (e.g., packages/react/accessible-icon or packages/react/avatar) — each has a src/ directory with a single main .tsx file named after the component
packages/react/{component-name}/src/index.tsCreate a barrel export file that re-exports everything from the main component file using 'export { ComponentName, type ComponentNameProps } from "./{component-name}"'
Pattern: See how packages/react/accessible-icon/src/index.ts re-exports from its main component file
packages/react/{component-name}/package.jsonCreate package.json with name '@radix-ui/react-{component-name}', define 'source', 'main', 'module', and 'types' fields pointing to src/index.ts and dist/ outputs. Add peerDependencies on react and react-dom. Add dependencies on any @radix-ui primitives used (e.g., @radix-ui/react-primitive, @radix-ui/react-use-controllable-state).
Pattern: Copy the package.json structure from packages/react/avatar/package.json — it shows the standard field layout, scripts, and dependency declarations
packages/react/{component-name}/tsconfig.jsonCreate a tsconfig.json that extends the shared base TypeScript config and sets compilerOptions and include paths for the src directory
Pattern: Copy the tsconfig.json from packages/react/avatar/tsconfig.json which extends the shared config
packages/react/radix-ui/src/index.tsAdd a re-export line for the new component: 'export { ComponentName, type ComponentNameProps } from "@radix-ui/react-{component-name}"' so it is available from the umbrella package
Pattern: See the existing re-export lines in packages/react/radix-ui/src/index.ts for every other component
packages/react/radix-ui/package.jsonAdd '@radix-ui/react-{component-name}' as a dependency in the radix-ui umbrella package
Pattern: See how packages/react/radix-ui/package.json lists all @radix-ui/react-* packages as dependencies
Conventions to follow:
Add a new shared React hook
packages/react/{hook-name}/src/{hook-name}.tsxCreate the hook implementation file. Export the hook as a named export. The hook name should follow the 'use-*' convention (e.g., use-controllable-state).
Pattern: Follow the structure in packages/react/use-controllable-state or packages/react/use-callback-ref — each has a src/ directory with the hook implementation
packages/react/{hook-name}/src/index.tsCreate a barrel export that re-exports the hook from the implementation file
Pattern: See how other hook packages like packages/react/use-callback-ref/src/index.ts handle their exports
packages/react/{hook-name}/package.jsonCreate package.json with name '@radix-ui/react-{hook-name}', standard source/main/module/types fields, and peerDependencies on react
Pattern: Copy the package.json structure from packages/react/use-callback-ref/package.json
packages/react/{hook-name}/tsconfig.jsonCreate tsconfig.json extending the shared base config
Pattern: Copy from packages/react/use-callback-ref/tsconfig.json
Conventions to follow:
Add a core utility function
packages/core/{utility-name}/src/{utility-name}.tsCreate the utility implementation file. Export pure functions with TypeScript types. Core utilities must be framework-agnostic (no React imports).
Pattern: Follow the structure in packages/core/number — it exports pure utility functions with full TypeScript typing
packages/core/{utility-name}/src/index.tsCreate barrel export re-exporting all public functions and types
Pattern: See packages/core/number/src/index.ts for the re-export pattern
packages/core/{utility-name}/package.jsonCreate package.json with name '@radix-ui/{utility-name}', no react peerDependencies since core packages are framework-agnostic
Pattern: Copy the package.json structure from packages/core/number/package.json
packages/core/{utility-name}/tsconfig.jsonCreate tsconfig.json extending the shared base config
Pattern: Copy from packages/core/number/tsconfig.json
Conventions to follow:
Add Storybook stories for a component
apps/storybook/stories/{component-name}.stories.tsxCreate a new stories file. Import the component from its package. Define a default export with title and component metadata. Create named exports for each story variant (e.g., Default, WithCustomProps, Controlled).
Pattern: Look at existing story files in apps/storybook/stories/ — each follows the CSF (Component Story Format) with a default meta export and named story exports
Conventions to follow:
Add Cypress e2e tests for a component
cypress/e2e/{component-name}.test.tsCreate a new Cypress test file. Use describe/it blocks. Visit the component's test page and interact with it using Cypress commands. Assert on DOM state, ARIA attributes, and keyboard interactions.
Pattern: Look at existing test files in cypress/e2e/ — they use cy.visit(), cy.get(), cy.findByRole() and assert on accessibility attributes
cypress/support/e2e.jsIf the component needs custom Cypress commands (e.g., for complex interactions), add them here using Cypress.Commands.add()
Pattern: See existing custom commands already registered in cypress/support/e2e.js
Conventions to follow:
Add SSR smoke test for a component
apps/ssr-testing/app/{component-name}/page.tsxCreate a new Next.js page that renders the component with typical props. The page should import the component from its @radix-ui package and render it in a basic layout. This verifies the component doesn't crash during server-side rendering.
Pattern: See existing pages in apps/ssr-testing/app/ — each is a simple page.tsx that imports and renders a component with minimal props
apps/ssr-testing/app/layout.tsxAdd a navigation link to the new component's SSR test page in the root layout's nav section so it's accessible from the index
Pattern: See how apps/ssr-testing/app/layout.tsx already lists links to existing component test pages
Conventions to follow:
Modify the build pipeline
internal/builder/builder.jsModify the build orchestration logic. This file handles compilation, bundling, and output generation for all packages. Add new build steps, modify output formats, or change compilation settings here.
Pattern: Read the existing build steps in internal/builder/builder.js to understand the pipeline stages: source reading, TypeScript compilation, bundling, and output writing
internal/builder/radix-build.jsIf adding new CLI flags or changing the build invocation interface, modify this entry point
Pattern: See how internal/builder/radix-build.js currently parses arguments and calls into builder.js
Conventions to follow:
Add or modify ESLint rules
internal/eslint-config/index.jsFor rules that should apply to ALL packages in the monorepo, add them to the shared config here. This is the centralized config imported by every package.
Pattern: See how internal/eslint-config/index.js structures its rule definitions and plugin configurations
internal/eslint-config/react-package.jsFor rules specific to React component packages, add them to this React-specific preset
Pattern: See how internal/eslint-config/react-package.js extends the base config with React-specific rules
internal/eslint-config/vite.jsFor rules specific to Vite-based projects, modify this config
Pattern: See how internal/eslint-config/vite.js layers Vite-specific rules on top of the base
eslint.config.mjsFor root-level overrides or workspace-wide ignores, modify the root ESLint config
Pattern: See how eslint.config.mjs imports from internal/eslint-config and adds project-level overrides
Conventions to follow:
Add a new primitive (low-level building block)
packages/react/{primitive-name}/src/{primitive-name}.tsxCreate the primitive implementation. Primitives are low-level building blocks used by multiple higher-level components. They should be minimal, composable, and handle a single concern (e.g., focus management, slot rendering, collection tracking).
Pattern: Study packages/react/primitive or packages/react/slot — primitives export minimal components/hooks that handle one specific DOM or React concern
packages/react/{primitive-name}/src/index.tsCreate barrel export for the primitive
Pattern: See packages/react/primitive/src/index.ts
packages/react/{primitive-name}/package.jsonCreate package.json. Primitives typically have minimal dependencies — only react, react-dom as peerDeps and possibly @radix-ui/react-primitive.
Pattern: Copy structure from packages/react/slot/package.json or packages/react/primitive/package.json
packages/react/{primitive-name}/tsconfig.jsonCreate tsconfig.json extending the shared base
Pattern: Copy from packages/react/primitive/tsconfig.json
Conventions to follow:
Modify global test configuration
scripts/setup-tests.tsAdd new global test utilities, custom matchers, or polyfills. This file runs before all Vitest tests across the monorepo.
Pattern: See how scripts/setup-tests.ts currently sets up matchers and polyfills — add new setup in the same style
types/global.d.tsIf the new test utilities introduce new global types or augment existing ones, declare them here
Pattern: See how types/global.d.ts declares existing global type augmentations
Conventions to follow:
Coding Conventions
Standards and patterns used in this codebase
Packages are compiled into three output formats (CommonJS, ESM, and TypeScript declarations) using esbuild and tsup via a shared builder utility
internal/builder/builder.js compiles each package to CJS, ESM, and .d.ts outputs; radix-build.js serves as the CLI entry point invoking the builder moduleESLint configs are centralized in an internal shared package and re-exported, with app-specific configs composing the shared base with additional rules (e.g., Storybook, Cypress)
internal/eslint-config/index.js aggregates JS, TS, and custom overrides; apps/storybook/eslint.config.js extends shared Vite rules with Storybook-specific lintingTesting is layered: Vitest for unit tests with a shared setup file (polyfills and testing-library initialization), Cypress for end-to-end tests of complex interactive components, and a dedicated Next.js SSR app for server-rendering smoke tests
scripts/setup-tests.ts initializes testing libraries and polyfills for Vitest; cypress.config.ts configures E2E viewport and parameters; apps/ssr-testing tests SSR renderingComponents follow a layered dependency architecture: core utilities (framework-agnostic) → foundation primitives (hooks, context, refs, slots) → infrastructure (overlay, focus, positioning) → composed components (dialog, menu, popover, form, etc.) → umbrella re-export package
packages/core provides number helpers and rect observation; packages/react builds hooks and primitives on top; higher-level packages like dialog and menu compose overlay infrastructure; radix-ui re-exports everythingGlobal TypeScript type definitions and module declarations are maintained in a centralized types directory, with shared TS configs managed in the internal infrastructure package
types/global.d.ts provides global type definitions and module declarations used across the monorepoThe repository uses a monorepo layout with apps/ for runnable applications (storybook, ssr-testing), packages/ for publishable libraries (core, react components), internal/ for build tooling and shared configs, and cypress/ for E2E tests
apps/storybook for visual development, apps/ssr-testing for SSR smoke tests, internal/builder for build tooling, internal/eslint-config for shared lint rulesConfiguration files use flat ESLint config format (eslint.config.js/mjs) and re-export patterns where wrapper files delegate to index modules for cleaner imports
internal/eslint-config/eslint.config.js re-exports from index.js as default export; root eslint.config.mjs composes shared configs with project-specific Cypress rules