Skip to main content
All articles
Guide July 10, 2026 9 min read

React Component Library Architecture in 2026

How modern React component libraries are structured: tree-shakeable packages, CSS-first theming, Radix internals, and auto-generated documentation.

By Ninna UI Team


The architecture of React component libraries has evolved significantly. In 2020, the dominant pattern was a single monolithic package with a JavaScript theme provider. In 2026, the pattern is multiple tree-shakeable packages with CSS-first theming. Let's look at how modern libraries are structured.

Package splitting

Ninna UI splits 67 components across 12 packages. The split is by concern: primitives (buttons, text), forms (inputs, selects), overlays (modals, tooltips), layout (stack, grid), navigation (tabs, accordions), data-display (cards, tables), feedback (alerts, toasts), and code-block.

The benefit is tree-shaking at the package level. If your app doesn't use overlays, you don't install @ninna-ui/overlays, and none of its code enters your bundle. With a monolithic package, tree-shaking works at the module level — but you still pay for the package's internal utilities.

The core package pattern

@ninna-ui/core is the foundation. It contains: the theme system (CSS custom properties, oklch color tokens, 5 preset themes), shared layout tokens (breakpoints, spacing, gap sizes), and the cn() class merging utility. Every component package depends on core, but packages don't depend on each other.

CSS-first theming

The theme system lives entirely in CSS. Each preset is a CSS file that defines custom properties on :root. Components reference these properties via Tailwind v4's theme system. There's no JavaScript theme object, no provider, and no runtime cost.

/* @ninna-ui/core/theme/presets/default.css */
:root {
  --color-primary: oklch(0.55 0.22 280);
  --color-primary-content: oklch(0.98 0.01 280);
  --color-base-100: oklch(0.98 0.005 280);
  /* ...8 semantic colors, each with content pair */
}

.dark {
  --color-primary: oklch(0.72 0.18 280);
  --color-base-100: oklch(0.18 0.02 280);
}

Radix UI as internal infrastructure

Complex widgets (Modal, Select, Tabs, Tooltip, DropdownMenu) are built on Radix UI primitives. But Radix is an internal dependency — wrapped through @ninna-ui/react-internal. Consumers never import from @radix-ui/* directly, and Radix types never leak into the public API.

This means: no Radix peer dependencies to manage, no version conflicts, and a consistent API across all components. When Radix fixes a bug, we update the wrapper and ship it via npm.

Auto-generated documentation

The llms.txt file, component API references, and prop tables are auto-generated from the actual package source code. A build script (scripts/generate-seo.js) parses the barrel exports, extracts prop interfaces, and generates structured documentation.

This ensures documentation is never out of sync with the code. When a new component is added or a prop changes, the next build regenerates the docs automatically.

The data-slot CSS API

Every component internal has a data-slot attribute — a stable, documented CSS hook. This lets consumers customize component internals without owning the source. There are 98 data-slot targets across all components, covering every customizable element.

Why this architecture matters

The 2026 architecture prioritizes: small bundles (tree-shakeable packages), fast hydration (CSS-first theming), accessibility (Radix internals), low maintenance (npm updates), and AI agent friendliness (auto-generated docs). These aren't independent features — they're consequences of the architectural choices.


Build it with Ninna UI

Accessible React components, CSS-only theming, Tailwind v4 native.

Get Started

Keep reading