UI libraries
Reusable interface parts: styled components you drop in and theme, or headless ones that handle state, keyboard and accessibility while you write the markup and styles.
2 technologies on this page ↓How it works
Most apps need the same parts: buttons, dialogs, menus, comboboxes, date pickers, data tables. Each part has two layers. The behaviour layer holds state (is the menu open, which row is selected, how the rows are sorted) and the interaction rules, such as the roles and keys the W3C’s ARIA Authoring Practices Guide sets out for each widget: in a combobox, Down Arrow moves into the popup and Escape closes it. The presentation layer is the markup and CSS. A component library ships both layers and implements one design system: Material UI renders Google’s Material Design, and you adjust it through a theme object (colours, spacing, shape) rather than rewriting its CSS. A headless library ships only the behaviour. TanStack Table hands you header groups and sorted rows and renders nothing; Radix Primitives, Headless UI, React Aria and Base UI render unstyled, accessible parts that you style with Tailwind, CSS Modules or plain CSS. A third route, shadcn/ui, copies styled component source into your repository instead of installing it as a package, with headless parts underneath. The trade is speed against control: a component library looks finished on day one but fights a design that differs from its own, while a headless one fits any design and leaves every element and style to you.
Furniture. A component library is a showroom set: sofa, table and chairs that match and arrive assembled, in the maker’s style. A headless library is the hardware: hinges, drawer runners and locks that work smoothly, fitted into cabinets you design yourself.
Reach for it when
- Internal tools and admin screens where shipping fast matters more than a distinctive look: a component library
- A product with its own design system: build it on headless parts so you own the markup and styles
- Widgets whose keyboard and screen reader behaviour is easy to get wrong by hand, such as comboboxes, menus, dialogs and data grids
- One behaviour across several frameworks: TanStack Table’s core has adapters for React, Vue, Solid, Svelte, Angular and five more
Think twice when
- Restyling a component library far away from its design system; MUI’s own team writes that Material UI “can become unwieldy” when a design diverges significantly
- A quick prototype on a headless library: you write and style every element yourself
- Mixing several component libraries in one app; shadcn/ui’s docs describe the usual result as wrapped components, style workarounds and incompatible APIs
A real example
A team needs an orders table with sorting, filtering and pagination. With a component library’s data grid they pass rows and columns and have a finished grid in an afternoon, though in MUI X multi-column sorting and column pinning sit in the paid Pro package and row grouping in Premium. With TanStack Table they register the sorting, filtering and pagination features, then render their own <table> with the design system’s classes. It takes more code, but the table matches every other screen, and the same sorted rows can drive a card list on mobile.
Kinds of ui libraries
Specific products, and what each does differently.
Component libraries →
Styled, ready-made components that implement one design system, which you theme rather than build.
Headless libraries →
State, logic and accessible behaviour for UI elements with no markup or styles of their own; you render and style everything.
| Component libraries | Open source | MUI | MIT (Material UI and MUI X Community); MUI X Pro and Premium are commercial | 2026-10-09 | |
| Headless libraries | Open source | TanStack | MIT | 2026-10-09 |
Three ways to get components#
What you install, and what is left for you to write.
| Component library | Headless library | Copy-in components | |
|---|---|---|---|
| Examples | Material UI, Bootstrap | TanStack Table and Form, Radix Primitives, Headless UI, React Aria, Base UI | shadcn/ui |
| What you get | Styled components and a theme | State, logic and accessible behaviour; some ship unstyled parts, some only functions and state | The source of styled components, added to your repository |
| Markup and CSS | Written by the library; you theme and override it | Written by you | Written by the tool, then owned and edited by you |
| Fixes and upgrades | Upgrade the package, including its breaking major versions | Upgrade the package; your markup is untouched | The headless layer updates as a dependency; the copied top layer is yours to merge |
| Fits best | Internal tools, prototypes, apps that can follow the library’s design system | Products with their own design system | Teams that want a styled start and full control afterwards |
In code · ts
// npm install @tanstack/table-core@9
import {
constructTable,
createSortedRowModel,
rowSortingFeature,
sortFns,
tableFeatures,
type ColumnDef,
} from '@tanstack/table-core'
import { storeReactivityBindings } from '@tanstack/table-core/store-reactivity-bindings'
type Order = { id: string; customer: string; total: number }
const data: Order[] = [
{ id: 'A-1', customer: 'Ada', total: 120 },
{ id: 'A-2', customer: 'Linus', total: 45 },
{ id: 'A-3', customer: 'Grace', total: 310 },
]
// the headless library holds the state and the sorting logic
const features = tableFeatures({
coreReactivityFeature: storeReactivityBindings(),
rowSortingFeature,
sortedRowModel: createSortedRowModel(),
sortFns,
})
const columns: ColumnDef<typeof features, Order>[] = [
{ accessorKey: 'id', header: 'Order' },
{ accessorKey: 'customer', header: 'Customer' },
{ accessorKey: 'total', header: 'Total' },
]
const table = constructTable({ features, columns, data })
// what a click on the “Total” header would call
table.getColumn('total')!.toggleSorting(true) // descending
// the markup is yours: here, plain text lines instead of <tr> elements
for (const row of table.getRowModel().rows) {
console.log(row.getAllCells().map((cell) => cell.getValue()).join(' '))
}
// A-3 Grace 310
// A-1 Ada 120
// A-2 Linus 45Often confused with
Where it came from2011–2026
Bootstrap set the pattern for component libraries. Two Twitter employees (@mdo and @fat) built it in mid-2010 as Twitter Blueprint, and it served as the company’s style guide for internal tools for over a year before its public release on 19 August 2011. In 2014 Material UI started as a React implementation of Google’s Material Design guidelines. Both gave teams one design language with ready-made parts, and both grew into some of the most widely used front-end projects.
Ready-made parts are hard to bend. In one MUI developer survey, five times as many developers as the year before were struggling to customize the components, and customization became the focus of Material UI v5. In June 2018 Merrick Christensen had described the other answer: a headless user interface component “offers maximum visual flexibility by providing no interface”. React Table v7 (March 2020) was rebuilt on that idea as a hooks-only utility that renders nothing.
Headless libraries for accessible widgets followed: Radix Primitives, Headless UI, React Aria, and in 2022 MUI Base (now Base UI), MUI’s headless alternative to its own library. shadcn/ui then combined the two models by copying styled component source, built on headless parts, into each project, citing AI models that read and improve the code as one more reason to own it.
Why it’s built this way
- Separate behaviour from presentation
- React Table v7’s release notes gave three reasons: the look of a table belongs to the app, dropping UI options removes a huge API surface to maintain, and an app can extend a design the library never imagined.
- Follow the WAI-ARIA patterns
- The W3C’s Authoring Practices Guide documents roles, states and keyboard rules for widgets such as comboboxes and menus. Libraries that implement them once save every app from re-deriving focus management and key handling.
- A theme object instead of editing CSS
- Material UI v1 moved from inline styles, which lacked media queries and pseudo-selectors, to CSS-in-JS with a JavaScript theme holding the palette, typography and breakpoints, so a brand could be applied in one place.
- Ship source, not a package
- shadcn/ui argues that installed libraries end in wrappers and style workarounds, so it hands you the component code to edit directly while the headless layer underneath still updates as a dependency.
Timeline
- 2011
Twitter releases Bootstrap publicly on 19 August
- 2014
Material UI starts as a React implementation of Material Design
- 2018
Merrick Christensen’s “Headless User Interface Components”; Material UI v1
- 2020
React Table v7 rebuilt as a headless, hooks-only utility
- 2022
MUI announces MUI Base, a headless alternative to Material UI
- 2025
Base UI 1.0 on 11 December
- 2026
TanStack Table v9 with opt-in features and ten framework adapters
- Bootstrap docs: About (history, Twitter Blueprint, released 19 August 2011)
- MUI blog: Material-UI is now MUI (September 2021; origin in 2014, 2020 survey on customization)
- MUI blog: Material UI v1 is out (May 2018; inline styles to CSS-in-JS, theme object)
- Merrick Christensen: Headless User Interface Components (June 2018)
- GitHub: React Table v7.0.0 release notes (March 2020)
- MUI blog: Introducing MUI Base, the headless alternative to Material UI (September 2022)
- Base UI docs: Releases (v1.0.0, 11 December 2025)
- TanStack blog: Announcing TanStack Table V9 (August 2026)
- W3C WAI: ARIA Authoring Practices Guide, Combobox pattern
- shadcn/ui docs: Introduction (open code, AI-ready, headless architecture)
Sources13
- W3C WAI: ARIA Authoring Practices Guide, Combobox pattern (Down Arrow, Escape)
- TanStack Table docs: Overview (headless UI; adapters for React, Preact, Octane, Vue, Solid, Svelte, Angular, Ember, Lit and Alpine)
- TanStack Table docs: Vanilla quick start (constructTable, tableFeatures, sorting)
- Material UI docs: Overview (implements Material Design 2)
- Material UI docs: How to customize (sx prop, theme overrides)
- MUI blog: Introducing MUI Base (Material UI “can become unwieldy” for divergent designs)
- MUI X docs: Licensing (Pro: multi-sorting and column pinning; Premium: row grouping)
- Radix Primitives docs: Introduction
- React Aria docs: Getting started
- GitHub: tailwindlabs/headlessui README
- Base UI docs: About (unstyled, works with Tailwind, CSS Modules or plain CSS)
- shadcn/ui docs: Introduction (“This is not a component library”)
- Bootstrap docs: About