Introduction
React components with a familiar API, React Aria's accessibility and plain CSS.
Hone UI is a free, open-source React component library. It uses the same component names, props,
sx prop and createTheme API as the popular React component library, so an existing app
can switch by changing its imports.
It works differently under the hood in three ways:
- No styling engine runs in the browser. A build plugin compiles
sxto plain CSS. - React Aria Components handle behavior and accessibility.
- Each component ships a guide written for AI coding tools. /llms.txt lists
them, and each one is served at
/ai/<Name>.md, such as /ai/Button.md.
Installation
Install the package and React Aria Components.
npm install @hone-ui/react react-aria-components
npm install -D @hone-ui/sxThe @hone-ui/sx plugin compiles sx to CSS. Set it up for your bundler.
Vite
Add the plugin before react(), and keep react() in the list so Fast Refresh still works.
import react from '@vitejs/plugin-react';
import sx from '@hone-ui/sx/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sx(), react()],
});Delete the template's src/index.css and src/App.css, and their imports. Their unlayered rules
beat the library's layered CSS. To keep them, put them in an earlier layer, as the
theming page shows.
Import one look at your app's root, such as src/main.tsx.
import '@hone-ui/react/styles/material.css'; // or minimal.cssNext.js
Wrap your config with withSx. Pass it the template's existing options so they survive, and turn
on optimizePackageImports. Without it, next build --webpack ships every component on every
route. Turbopack does not need it.
import { withSx } from '@hone-ui/sx/next';
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
// Keep the options create-next-app generated, such as cacheComponents.
experimental: { optimizePackageImports: ['@hone-ui/react'] },
};
export default withSx(nextConfig);Unlayered CSS beats every rule in the library's layers, and the compiled sx too. The
app/globals.css file from create-next-app has an unlayered * { margin: 0; padding: 0 } reset
and body rules. They collapse component spacing and leave the body white in dark mode. Delete
the file and its import, or wrap its rules in a layer that ranks first:
@layer reset, hone-ui;
@layer reset {
* {
margin: 0;
padding: 0;
}
}The @layer reset, hone-ui; line works only if globals.css loads before the look, because the
look's CSS declares the hone-ui layer the moment it loads. Import globals.css first in the
layout.
import './globals.css';
import '@hone-ui/react/styles/material.css'; // or minimal.css
import InitColorSchemeScript from '@hone-ui/react/InitColorSchemeScript';
import { AppTheme } from './theme';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<InitColorSchemeScript />
<AppTheme>{children}</AppTheme>
</body>
</html>
);
}Client-side links
Button href renders a link that reloads the page by default. To navigate on the client, wrap
the app once in React Aria's RouterProvider, inside your 'use client' theme file.
'use client';
import { createTheme, ThemeProvider } from '@hone-ui/react/styles';
import { useRouter } from 'next/navigation';
import type { ReactNode } from 'react';
import { RouterProvider } from 'react-aria-components';
const theme = createTheme();
export function AppTheme({ children }: { children: ReactNode }) {
const router = useRouter();
return (
<RouterProvider navigate={router.push}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</RouterProvider>
);
}Ctrl-click and Cmd-click still open a new tab. In React Router, pass useNavigate() instead.
Theme provider
Wrap your app in ThemeProvider, and add CssBaseline for the page background and font.
import { CssBaseline, createTheme, ThemeProvider } from '@hone-ui/react';
const theme = createTheme();
export function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{/* your app */}
</ThemeProvider>
);
}In Next.js, put the provider in a file marked 'use client', as app/theme.tsx above does. The
theming page covers themes in full.
Requirements
React 19 and react-aria-components 1.21 or later. The packages ship their own types and are
built and tested with TypeScript 6.0. TypeScript 7.0.2 also compiled a sample app. The packages
set no typescript peer range, so no version is enforced.
Canary builds
Each merge to main with pending changes publishes a canary under the canary tag, for example
npm install @hone-ui/react@canary. With pnpm 10.16 or later, minimumReleaseAge can block a
fresh canary because it is only minutes old. Exclude the hone-ui packages in
pnpm-workspace.yaml:
minimumReleaseAgeExclude:
- '@hone-ui/*'
- hone-uiStatus
The library is in early development. It exports 75 components and parts, such as DialogTitle
and TableRow, from Button and TextField to Dialog and Select. The components page
has a guide with examples, a props table and notes for each main component.
Not included yet
Container, Grid, Link, ButtonGroup, ToggleButton, Breadcrumbs, Stepper, Rating, Popover, Modal,
Fab and an icons package. Use Box, Stack and the sx prop for layout in the meantime.