Skip to content

Product screens,already designed

ferry-ui is a React design system for dashboards, admin consoles and developer tools. You get the tokens, the controls, the page patterns and the application shell, so you start from a finished screen.

Screens built with ferry-ui

This is not a picture. Click in the screen. The application frame with info tiles, metric cards, a grid of projects and a command menu.

See how it is built

Built on the stack you already use

  • React 19
  • TypeScript
  • Tailwind CSS v4
  • Radix UI
  • shadcn/ui
  • Lucide
  • Vite
  • Next.js
  • React Router
  • TanStack Router
  • Storybook
  • Vitest

Every layer of the screenTry each one here

28 accessible primitives

Buttons, fields, menus, dialogs and tables on Radix UI. The sizes have the same names in every control, so a row of controls lines up.

One set of tokens

Each color, radius and font is a CSS variable with a Tailwind class. The dark theme is one class on the page.

  • surface-75
  • surface-200
  • border-strong
  • foreground
  • primary-solid
  • success
  • warning
  • destructive
Try the other theme

22 patterns

Page headers, form cards, table states, empty states and confirm dialogs: the parts you build again in each product.

Billing portalActive

The application shell

A top bar, an icon rail, a phone drawer and a command menu, all from one list of pages.

Keyboard and screen readers

Focus, ARIA names and keys come with the components. Put the mouse away and try this menu.

Move the focus to the button
Tab
Open the menu
Enter
Move in the menu
↑↓
Close the menu
Esc

Written for your AI agent too

The repository has an AGENTS.md guide with rules and type-checked snippets. Each page of these docs has a Markdown version, and llms.txt lists them.

llms.txt
# ferry-ui

> ferry-ui is a React 19 design system for product interfaces.

## Primitives

- [Button](/docs/components/button.md): A control that starts an action.
- [Dialog](/docs/components/dialog.md): A window on top of the page.

Your router, your state

A link is an href string that goes through one adapter. A component holds no global state: you control it with props.

providers.tsx
import { Link } from 'react-router'
import { LinkProvider, type LinkComponent } from 'ferry-ui'

const RouterLink: LinkComponent = ({ href, ...props }) => (
  <Link to={href} {...props} />
)

<LinkProvider component={RouterLink}>{app}</LinkProvider>

One package, one import. The page you read now uses the same components: its navigation, its search, its code blocks and its tables are ferry-ui.

Add it to your appin four steps

You need React 19. Tailwind CSS v4 is optional: without it, you import one compiled stylesheet.

  1. Install the package

    One package, with the components, the tokens and the stylesheets.

    $ npm install ferry-ui
  2. Import the styles

    After Tailwind CSS v4, import the theme. It brings the tokens and the classes of the components.

    app.css
    @import "tailwindcss";
    @import "ferry-ui/fonts.css"; /* optional: Inter and Source Code Pro */
    @import "ferry-ui/theme.css";
  3. Mount the providers

    One time, at the root of the app: the theme, the tooltips and the toasts.

    providers.tsx
    import type { ReactNode } from 'react'
    import { ThemeProvider, Toaster, TooltipProvider } from 'ferry-ui'
    
    export function Providers({ children }: { children: ReactNode }) {
      return (
        <ThemeProvider defaultTheme="system">
          <TooltipProvider>
            {children}
            <Toaster />
          </TooltipProvider>
        </ThemeProvider>
      )
    }
  4. Use a component

    Import each component from the package root.

    no-projects.tsx
    import { Button, EmptyState } from 'ferry-ui'
    import { FolderKanban, Plus } from 'lucide-react'
    
    export function NoProjects({ onCreate }: { onCreate: () => void }) {
      return (
        <EmptyState
          icon={<FolderKanban />}
          title="No projects yet"
          description="Projects group your invoices, members and API keys."
          actions={
            <Button variant="primary" icon={<Plus />} onClick={onCreate}>
              New project
            </Button>
          }
        />
      )
    }

No Tailwind in your app?

Import ferry-ui/styles.css at the root of the app. Style your own markup with plain CSS and the variables of the tokens.

Read about styling

Your colors, your fonts

Override a few CSS variables after the import to give ferry-ui the look of your product, in light and in dark.

Read about theming

Open source, MIT licensedRead it, run it, change it

ferry-ui is built in the open. Each component has a JSDoc that says when to use it and when not to, a story in Storybook and a page in these docs.

What is in the box
  • TokensColors, radii, shadows and fonts, light and dark
  • Primitives28 building blocks on Radix UI
  • Patterns22 compositions for recurring product needs
  • Layout8 pieces of the application shell
  • Hooks and helperscn, useCopy, useTheme, the link contract
  • DocumentationThese docs, a Storybook and a guide for agents

Product screens, already designed

Read the quick start, copy a page from the examples and put your data in it.