Esc

Components

All components Every component, with search. Accordion Sections that expand and collapse, one at a time or several. Alert A highlighted message, optionally dismissible. Avatar A person's picture, with initials when there is none. Avatar group Overlapping avatars with a "+N" count for the rest. Badge A small status label. Banner A dismissible strip across the top of a page. Breadcrumb The path to the current page, collapsing long trails. Button Buttons in every variant and size, with icons and a loading state. Button to A button that submits a one-off form, like Rails' button_to. Card A bordered container with header, content and footer. Checkbox A checkbox with label, standalone or bound to a form builder. Collapsible A section that opens and closes under its trigger, like a branch of a nav tree. Data grid A sortable, filterable, server-paged table on AG Grid. Date picker A typed date field with a calendar, on Cally. Dialog A modal dialog on the native <dialog> element. Divider A horizontal or vertical rule, optionally labelled. Form field A label, hint and error message wired to any form control. Icon A Lucide icon. Image An image with consistent sizing and corner radius. Input A text field with label, hint and error message, standalone or with a form builder. Input group A text field with addons before or after it. Label A form label with a required marker. Link A link styled as text or as a button. Menu A dropdown menu with keyboard navigation. Pagination Page links for a long list, from a Pagy object. Password input A password field with a show/hide toggle. Popover Floating content anchored to a trigger. Radio group A set of radio buttons. Native select The browser's own select, styled closed; best for phones' system pickers. Select A styled dropdown with keyboard navigation and type-ahead, posting like a native select. Sheet A panel that slides in from an edge of the screen. Sidebar A collapsible application sidebar with groups and menus. Spinner A loading indicator. Switch An on/off toggle. Table A styled table with header, rows and caption. Table of contents An "On this page" list that follows the reader's scroll. Tabs Tabbed panels with keyboard navigation. Text Body text in set sizes, weights and tones. Textarea A multi-line text field with label, hint and error, that can grow with its text. Theme toggle A light/dark/system theme switcher. Tooltip A hint shown on hover and focus. Typography Headings, lead, muted, code and blockquote text styles.

Build with AI

Most UI is written by an agent now. VelvetKit gives it the context to use your components correctly, so it doesn't invent new ones.

Let your agent install it

Paste this into Claude Code, Cursor, Codex or any agent working in your Rails app:

Prompt
Set up VelvetKit in this Rails app. Add gem "velvetkit", group: :development to the Gemfile, run bundle install, then run bin/rails generate velvetkit:install. Read https://velvetkit.com/llms.txt, then add the components this app needs with bin/rails generate velvetkit:add and use them for all new UI.

It runs the same commands as Getting started, then reads llms.txt to learn what the kit has.

In your repo

velvetkit:install writes three files, so an agent finds the kit without being told:

File Read by What's in it
docs/velvetkit.md Any agent The rules for building UI with the kit, then every installed component: arguments, slots, notes and working examples.
.claude/skills/velvetkit/SKILL.md Claude Code A skill that loads when Claude writes views, and points it at docs/velvetkit.md.
AGENTS.md Codex, Cursor, Copilot, … A short section pointing at docs/velvetkit.md. Your own notes are kept.

Here's the start of it for an app using the Kit namespace with a dialog installed. Nothing on it says UI::, so the agent writes the code your app runs.

docs/velvetkit.md
# VelvetKit components in this app

This app builds its UI from VelvetKit components: ViewComponents styled with
Tailwind CSS 4 and wired up with Stimulus. They were copied into the app by
the `velvetkit` gem and are now the app's own code, under `Kit::`.

Use them for every new screen. Reach for raw HTML and Tailwind only when no
component fits, and never build a second version of a component that exists.

## Where things are

- Components: `app/components/kit/` (`Kit::Button` is `app/components/kit/button.rb`)
- Their Stimulus controllers: `app/javascript/controllers/kit/`, registered as `kit--<name>`
- Tests: `test/components/kit/` (Minitest, `render_inline`)
- Lookbook previews: `test/components/previews/kit/`
- Design tokens: `app/assets/tailwind/velvetkit.css`
- Strings: `config/locales/velvetkit.en.yml`, under `kit.`
- Icons: `app/javascript/velvetkit_icons.ts`

## Rules

1. **Render, with slots.** Components take keyword arguments and fill their
   parts through `with_*` slots in a block:

   ```erb
   <%= render(Kit::Card.new) do |card| %>
     <% card.with_header do |header| %>
       <% header.with_title { "Billing" } %>
     <% end %>
     <% card.with_content { "…" } %>
   <% end %>
   ```

2. **Pass `class:` to adjust, not to restyle.** Your classes are merged with
   the component's by tailwind_merge, and yours win on conflict. For a
   different look, use a `variant:` or `size:` the component already has.

3. **Never hardcode colours.** Use the tokens: `bg-primary`,
   `text-muted-foreground`, `border-input`, `bg-card`, `text-destructive`.
   They switch with dark mode. Hex values and `blue-600` don't.

4. **Need a component the app doesn't have yet?** Add it from the kit rather
   than writing one: `bin/rails generate velvetkit:add <name>`. It copies the
   component with everything it depends on, and refreshes this file.
   Available: accordion, alert, avatar, avatar_group, badge, banner, breadcrumb, button, button_to, card, checkbox, collapsible, data_grid, date_picker, dialog, divider, form_field, icon, image, input, input_group, label, link, menu, pagination, password_input, popover, radio_group, native_select, select, sheet, sidebar, spinner, switch, table, table_of_contents, tabs, text, textarea, theme_toggle, tooltip, typography.

5. **Icons are Lucide names** (`Kit::Icon.new(name: "calendar")`). An icon the
   app hasn't used before must be added to `app/javascript/velvetkit_icons.ts`,
   in both the import list and the `icons` object, or it renders empty.

6. **User-facing text goes through I18n**, as the components' own strings do.

7. **Changing a component is fine**, it's app code. Keep its test passing, and
   update its Lookbook preview so the change can be seen.

8. **Links that look like buttons** use `Kit::Link.new(href:, variant:, button_variant: true)`.
   Without `button_variant: true` a link is styled as text.

## Components installed here


…

On the web

Everything on this site is also published as plain text for agents:

Prompts that work

Name the outcome and let the reference supply the components. A few that go well:

Build a settings page for notifications with our UI components. Read docs/velvetkit.md first.
Before deleting a project, ask for confirmation in a dialog. Use the kit's dialog, adding it with velvetkit:add if it isn't installed.
Our event form needs a date field. Add the date picker from VelvetKit and use it there.
Change the primary colour to #0F766E everywhere. Change the tokens in velvetkit.css, not the components.