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.
Your agent installs it
One prompt sets the kit up and teaches the agent where to read.
A reference in your repo
docs/velvetkit.md: what's installed, in your namespace.
Docs agents can read
llms.txt, and Markdown for every page.
Let your agent install it
Paste this into Claude Code, Cursor, Codex or any agent working in your Rails app:
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. |
It stays current
velvetkit:add rewrites the reference to match what's installed. To regenerate it by hand, run
bin/rails generate velvetkit:ai. To leave it out, install with --skip-ai.
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.
# 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.