Skill125 repo starsupdated 2mo ago
frappe-impl-ui-components
>
Install in Claude Code
Copygit clone --depth 1 https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package /tmp/frappe-impl-ui-components && cp -r /tmp/frappe-impl-ui-components/skills/source/impl/frappe-impl-ui-components ~/.claude/skills/frappe-impl-ui-componentsThen start a new Claude Code session; the skill loads automatically.
Definition
SKILL.md
# Frappe UI Components & Realtime — Implementation Workflows
Step-by-step workflows for building client-side UI. For form scripting see `frappe-impl-clientscripts`. For server-side API see `frappe-syntax-serverscripts`.
**Version**: v14/v15/v16 | **Note**: v15+ uses Bootstrap 5; Dialog API is stable across all versions.
## Quick Decision: Which UI Component?
```
WHAT do you need?
├── Prompt user for input → frappe.prompt (simple) or frappe.ui.Dialog (complex)
├── Show a message/alert → frappe.msgprint / frappe.show_alert / frappe.throw
├── Confirm an action → frappe.confirm
├── Multi-field data entry popup → frappe.ui.Dialog with fields
├── Select from a list of records → frappe.ui.form.MultiSelectDialog
├── Full custom page (not a form) → frappe.ui.Page
├── Customize list columns/colors → frappe.listview_settings
├── Visual board for workflow → Kanban Board (Select field based)
├── Date-based record view → Calendar View ({doctype}_calendar.js)
├── Hierarchical data display → Tree View (is_tree DocType)
├── Live updates without refresh → frappe.publish_realtime + frappe.realtime.on
├── Show background job progress → frappe.publish_progress
├── Scan barcode/QR code → frappe.ui.Scanner
└── Custom cell formatting → formatters in listview_settings or form
```
See `references/decision-tree.md` for the complete decision tree.
## Workflow 1: Dialogs (frappe.ui.Dialog)
### Simple Dialog
```javascript
let d = new frappe.ui.Dialog({
title: "Enter Details",
fields: [
{ label: "Full Name", fieldname: "full_name", fieldtype: "Data", reqd: 1 },
{ label: "Email", fieldname: "email", fieldtype: "Data", options: "Email" },
{ label: "Role", fieldname: "role", fieldtype: "Select",
options: "Developer\nManager\nDesigner" },
],
size: "small", // "small", "large", or "extra-large"
primary_action_label: "Create",
primary_action(values) {
frappe.call({
method: "myapp.api.create_user",
args: values,
callback(r) {
if (!r.exc) {
frappe.show_alert({ message: "User created", indicator: "green" });
d.hide();
}
}
});
}
});
d.show();
```
**Rule**: ALWAYS call `d.hide()` inside the callback, NEVER before the async call completes.
### Dialog with Table Field
```javascript
let d = new frappe.ui.Dialog({
title: "Add Items",
fields: [
{ label: "Customer", fieldname: "customer", fieldtype: "Link",
options: "Customer", reqd: 1 },
{ fieldtype: "Section Break" },
{ label: "Items", fieldname: "items", fieldtype: "Table",
in_place_edit: true, reqd: 1,
fields: [
{ fieldname: "item", label: "Item", fieldtype: "Link",
options: "Item", in_list_view: 1, reqd: 1 },
{ fieldname: "qty", label: "Qty", fieldtype: "Int",
in_list_view: 1, default: 1 },
{ fieldname: "rate", label: "Rate", fieldtype: "Currency",
in_list_view: 1 },
],
},
],
primary_action_label: "Submit",
primary_action(values) {
console.log(values); // { customer: "...", items: [{item, qty, rate}] }
d.hide();
}
});
d.show();
```
**Rule**: ALWAYS set `in_list_view: 1` on table child fields you want visible. Fields without it are hidden in the grid.
### Multi-Step Dialog
```javascript
let d = new frappe.ui.Dialog({
title: "Setup Wizard",
fields: [
// Page 1
{ fieldtype: "Section Break", label: "Step 1: Basic Info",
collapsible: 0 },
{ label: "Name", fieldname: "name", fieldtype: "Data", reqd: 1 },
// Page 2
{ fieldtype: "Section Break", label: "Step 2: Configuration",
collapsible: 0 },
{ label: "Option", fieldname: "option", fieldtype: "Select",
options: "A\nB\nC" },
],
primary_action_label: "Finish",
primary_action(values) {
d.hide();
}
});
d.show();
```
### Key Dialog Methods
| Method | Purpose |
|--------|---------|
| `d.show()` | Display the dialog |
| `d.hide()` | Close the dialog |
| `d.get_values()` | Get all field values as object |
| `d.set_values({field: val})` | Set field values |
| `d.get_field("name")` | Get a specific field control |
| `d.set_df_property("name", "hidden", 1)` | Show/hide fields dynamically |
| `d.disable_primary_action()` | Grey out submit button |
| `d.enable_primary_action()` | Re-enable submit button |
## Workflow 2: Messages & Alerts
### frappe.msgprint: Modal Message
```javascript
// Simple message
frappe.msgprint("Record saved successfully");
// With options
frappe.msgprint({
title: "Warning",
message: "This action cannot be undone",
indicator: "orange", // green, blue, orange, red
primary_action: {
label: "Proceed",
action() { do_something(); }
}
});
// List of messages
frappe.msgprint({
title: "Validation Errors",
message: "Please fix the following:",
as_list: true,
indicator: "red",
});
```
### frappe.throw: Error with Exception
```javascript
// Client-side: shows msgprint and stops execution
frappe.throw("Amount cannot be negative");
```
```python
# Server-side: raises ValidationError, shown as red msgprint
frappe.throw("Amount cannot be negative")
frappe.throw("Not Permitted", frappe.PermissionError) # specific exception
```
**Rule**: ALWAYS use `frappe.throw` for validation errors. NEVER use `frappe.msgprint` for errors — it does not stop execution.
### frappe.confirm: Yes/No Dialog
```javascript
frappe.confirm(
"Are you sure you want to delete this record?",
() => { /* Yes callback */ delete_record(); },
() => { /* No callback (optional) */ }
);
```
### frappe.prompt: Quick Single-Field Input
```javascript
frappe.prompt(
{ label: "Reason", fieldname: "reason", fieldtype: