frappe-ui
Build UIs that look and feel like Frappe products by composing frappe-ui components and styling with the library's semantic Tailwind tokens. Never hand-roll buttons, inputs, dialogs, dropdowns, etc. — pick the right component first.
Quick start
<script setup>
import { Button, Dialog, TextInput, FormControl } from 'frappe-ui'
import { ref } from 'vue'
const open = ref(false)
const name = ref('')
</script>
<template>
<div class="p-4 bg-surface-base text-ink-gray-9">
<Button variant="solid" theme="gray" @click="open = true">New Task</Button>
<Dialog v-model:open="open" title="Create Task">
<FormControl v-model="name" label="Title" required />
</Dialog>
</div>
</template>
Rules
Each rule states what to do and what to avoid — one canonical place, no separate anti-pattern list.
- Pick the component, don't build one. Consult
COMPONENTS.md; only fall back to raw HTML for layout (grids, flex). Never hand-roll <button class="bg-blue-500 …">.
- Semantic tokens, not raw colors.
bg-surface-*, text-ink-*, border-outline-* — never bg-gray-100, text-gray-900, border-gray-300. See TOKENS.md.
- Color =
variant + theme. variant (solid | outline | subtle | ghost) + theme (gray | blue | green | red | orange). Never invent intent / kind / severity / appearance.
- Two-way state via
v-model. Inputs v-model; overlays v-model:open; comboboxes v-model + v-model:query. Never :value + @change, never bare v-model on <Dialog>. Writes: immediate: false + submit(params).
- Use the input labeling contract. Every control accepts
label, description, error, required — use them, not placeholder-as-label or a separate <label>.
- Slot vocabulary is fixed.
#prefix, #suffix, #trigger, #empty, #header, #footer, #default; per-item #item-prefix / #item-suffix. No #icon-left / #avatar-right.
- Icons are CSS classes.
<span class="lucide-<name> size-4" aria-hidden="true" />; for icon props pass the string "lucide-edit". Never import per-icon Vue components. See COMPONENTS.md → Icons.
- Imperative for one-shot UI.
dialog.confirm / alert / prompt, toast.success / error / info — don't hand-mount <Dialog> to ask "are you sure?".
- API calls go through
useCall (or useList / useDoc). Never fetch / axios; don't reach for the legacy createResource family in new code. See COMPONENTS.md → Data & resources.
- Style components via
data-slot / data-state, not class injection. No triggerClass / contentClass props — they don't exist by design. See TOKENS.md → Custom CSS hooks.
- Bootstrapping from scratch? Follow
SETUP.md exactly — version pins (Tailwind v3, Vite 5), exports subpaths, optimizeDeps.exclude: ['frappe-ui'], app.use(FrappeUI), and vue-router are all required and easy to miss.
Reference files
- SETUP.md — scaffolding a fresh Vite + Vue 3 + frappe-ui project: version pinning,
vite.config.js, Tailwind, PostCSS, CSS entry, plugin vs provider. Read this first when bootstrapping from scratch.
- COMPONENTS.md — component catalog: when to reach for each one, key props, common pitfalls.
- TOKENS.md — semantic color tokens (
ink-*, surface-*, outline-*), typography, spacing, radii.
- PATTERNS.md — recipes: form pages, list pages, settings panels, empty states, confirmation flows.
Authoritative upstream docs
When the bundled refs don't answer a specific API question, fetch the official LLM-friendly index:
- https://ui.frappe.io/llms.txt — curated index of every component doc, design-system tokens page, and data-fetching guide. Always current with the published library; follow the links inside for full details on a specific component.
Prefer the upstream llms.txt over guessing — it lists every component's docs page and the canonical design-system / data-fetching pages. Source lives in the frappe/frappe-ui GitHub repo (src/components/<Name>/, plus PHILOSOPHY.md and CONTEXT.md at the repo root).
Source: frappe/frappe-ui — distributed by TomeVault.
1---2name: frappe-ui3description: Build consistent Frappe-style user interfaces using the frappe-ui Vue 3 component library and its design tokens. Use when scaffolding pages, forms, dialogs, lists, or any UI inside a Frappe-based app, when the user mentions frappe-ui, Frappe Cloud / Gameplan / Desk / Drive / Insights styling, or asks to "use frappe-ui components". Use when this capability is needed.4---56# frappe-ui78Build UIs that look and feel like Frappe products by composing **frappe-ui** components and styling with the library's **semantic Tailwind tokens**. Never hand-roll buttons, inputs, dialogs, dropdowns, etc. — pick the right component first.910## Quick start1112```vue13<script setup>14import { Button, Dialog, TextInput, FormControl } from 'frappe-ui'15import { ref } from 'vue'1617const open = ref(false)18const name = ref('')19</script>2021<template>22 <div class="p-4 bg-surface-base text-ink-gray-9">23 <Button variant="solid" theme="gray" @click="open = true">New Task</Button>24 <Dialog v-model:open="open" title="Create Task">25 <FormControl v-model="name" label="Title" required />26 </Dialog>27 </div>28</template>29```3031## Rules3233Each rule states what to do and what to avoid — one canonical place, no separate anti-pattern list.34351. **Pick the component, don't build one.** Consult `COMPONENTS.md`; only fall back to raw HTML for layout (grids, flex). Never hand-roll `<button class="bg-blue-500 …">`.362. **Semantic tokens, not raw colors.** `bg-surface-*`, `text-ink-*`, `border-outline-*` — never `bg-gray-100`, `text-gray-900`, `border-gray-300`. See `TOKENS.md`.373. **Color = `variant` + `theme`.** `variant` (`solid | outline | subtle | ghost`) + `theme` (`gray | blue | green | red | orange`). Never invent `intent` / `kind` / `severity` / `appearance`.384. **Two-way state via `v-model`.** Inputs `v-model`; overlays `v-model:open`; comboboxes `v-model` + `v-model:query`. Never `:value` + `@change`, never bare `v-model` on `<Dialog>`. Writes: `immediate: false` + `submit(params)`.395. **Use the input labeling contract.** Every control accepts `label`, `description`, `error`, `required` — use them, not placeholder-as-label or a separate `<label>`.406. **Slot vocabulary is fixed.** `#prefix`, `#suffix`, `#trigger`, `#empty`, `#header`, `#footer`, `#default`; per-item `#item-prefix` / `#item-suffix`. No `#icon-left` / `#avatar-right`.417. **Icons are CSS classes.** `<span class="lucide-<name> size-4" aria-hidden="true" />`; for icon props pass the string `"lucide-edit"`. Never import per-icon Vue components. See `COMPONENTS.md` → Icons.428. **Imperative for one-shot UI.** `dialog.confirm` / `alert` / `prompt`, `toast.success` / `error` / `info` — don't hand-mount `<Dialog>` to ask "are you sure?".439. **API calls go through `useCall`** (or `useList` / `useDoc`). Never `fetch` / `axios`; don't reach for the legacy `createResource` family in new code. See `COMPONENTS.md` → Data & resources.4410. **Style components via `data-slot` / `data-state`, not class injection.** No `triggerClass` / `contentClass` props — they don't exist by design. See `TOKENS.md` → Custom CSS hooks.4511. **Bootstrapping from scratch?** Follow `SETUP.md` exactly — version pins (Tailwind v3, Vite 5), `exports` subpaths, `optimizeDeps.exclude: ['frappe-ui']`, `app.use(FrappeUI)`, and `vue-router` are all required and easy to miss.4647## Reference files4849- [SETUP.md](SETUP.md) — scaffolding a fresh Vite + Vue 3 + frappe-ui project: version pinning, `vite.config.js`, Tailwind, PostCSS, CSS entry, plugin vs provider. Read this first when bootstrapping from scratch.50- [COMPONENTS.md](COMPONENTS.md) — component catalog: when to reach for each one, key props, common pitfalls.51- [TOKENS.md](TOKENS.md) — semantic color tokens (`ink-*`, `surface-*`, `outline-*`), typography, spacing, radii.52- [PATTERNS.md](PATTERNS.md) — recipes: form pages, list pages, settings panels, empty states, confirmation flows.5354## Authoritative upstream docs5556When the bundled refs don't answer a specific API question, fetch the official LLM-friendly index:5758- **https://ui.frappe.io/llms.txt** — curated index of every component doc, design-system tokens page, and data-fetching guide. Always current with the published library; follow the links inside for full details on a specific component.5960Prefer the upstream `llms.txt` over guessing — it lists every component's docs page and the canonical design-system / data-fetching pages. Source lives in the `frappe/frappe-ui` GitHub repo (`src/components/<Name>/`, plus `PHILOSOPHY.md` and `CONTEXT.md` at the repo root).6162---63> Source: [frappe/frappe-ui](https://github.com/frappe/frappe-ui) — distributed by [TomeVault](https://tomevault.io).64<!-- tomevault:4.0:skill_md:2026-06-27 -->