Use when creating or customizing Workspace pages in Frappe v14-v16. Covers Workspace DocType structure, shortcuts, number cards, dashboard charts, custom HTML blocks, JSON content format, shipping workspaces with custom apps, and role-based access control. Prevents common mistakes with content/child-table desync and missing fixtures. Keywords: workspace, desk, dashboard, number card, chart, shortcut,, customize desk, dashboard setup, add shortcut, module page, sidebar customize. workspace bui...
Scanned 9/6/2026
Install to Claude Code
npx -y skills add OpenAEC-Foundation/ERPNext_Anthropic_Claude_Development_Skill_Package --skill frappe-impl-workspace --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Frappe Impl Workspace?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/openaec-foundation-frappe-impl-workspace)More formats (shields.io, HTML) on the badges page.
---
name: frappe-impl-workspace
description: >
Use when creating or customizing Workspace pages in Frappe v14-v16.
Covers Workspace DocType structure, shortcuts, number cards, dashboard
charts, custom HTML blocks, JSON content format, shipping workspaces
with custom apps, and role-based access control.
Prevents common mistakes with content/child-table desync and missing fixtures.
Keywords: workspace, desk, dashboard, number card, chart, shortcut,, customize desk, dashboard setup, add shortcut, module page, sidebar customize.
workspace builder, module, fixtures, sidebar.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
author: OpenAEC-Foundation
version: "3.0"
---
# Frappe Workspace Implementation Workflow
Step-by-step workflows for creating and customizing Workspace pages. Workspaces are the block-based dashboard/navigation pages in Frappe Desk.
**Version**: v14/v15/v16 (version-specific features noted)
---
## Quick Reference
| Concept | Description |
|---------|-------------|
| Workspace | Block-based page with 12-column grid layout |
| Public Workspace | Visible to all permitted users; requires Workspace Manager role to edit |
| Private Workspace | Per-user dashboard under "My Workspaces"; any Desk User can create |
| Content field | JSON array storing the block layout |
| Child tables | 6 tables: charts, shortcuts, links, quick_lists, number_cards, custom_blocks |
| Module association | Primary access control mechanism |
---
## Master Decision: What Do You Need?
```
NEED A WORKSPACE?
│
├─► Default DocType landing page?
│ └─► NO workspace needed — Frappe auto-generates list views
│
├─► Custom dashboard for a module?
│ └─► Create PUBLIC Workspace (Workspace Manager role required)
│
├─► Personal dashboard for a user?
│ └─► Create PRIVATE Workspace (appears under "My Workspaces")
│
└─► Navigation link in sidebar?
└─► type="Link" (internal) or type="URL" (external)
ADDING COMPONENTS?
│
├─► Key metrics (counts, sums) → Number Cards
├─► Trend / time-series data → Dashboard Charts
├─► Quick navigation links → Shortcuts
├─► Grouped link categories → Link Cards (Card Break + Links)
├─► Custom HTML/JS content → Custom HTML Blocks
└─► Recent record lists → Quick Lists
```
---
## Workspace DocType Structure
### Key Fields
| Field | Type | Purpose |
|-------|------|---------|
| `label` | Data | Display name in sidebar |
| `title` | Data | Page title (defaults to label) |
| `module` | Link → Module Def | Associates workspace with a module for access control |
| `parent_page` | Link → Workspace | Nesting under another workspace in sidebar |
| `icon` | Data | Sidebar icon (e.g., `"chart-line"`) |
| `type` | Select | `Workspace` / `Link` / `URL` (v15+) |
| `sequence_id` | Int | Sidebar ordering |
| `content` | JSON | Block layout as JSON array |
| `for_user` | Data | If set, workspace is private to that user |
| `roles` | Table → Has Role | Role-based access restrictions |
| `app` | Data | Owning app identifier (v15+) |
| `indicator_color` | Color | Sidebar indicator dot (v15+) |
### Child Tables (6 total)
| Child Table | DocType | Purpose |
|-------------|---------|---------|
| `charts` | Workspace Chart | Dashboard Chart references |
| `shortcuts` | Workspace Shortcut | DocType/Report/Page/URL shortcuts |
| `links` | Workspace Link | Grouped navigation links |
| `quick_lists` | Workspace Quick List | Recent record lists |
| `number_cards` | Workspace Number Card | Metric card references |
| `custom_blocks` | Workspace Custom Block | HTML block references |
> **CRITICAL**: The `content` JSON and the child tables MUST stay in sync. ALWAYS use the Workspace Builder UI or programmatic API — NEVER manually edit the `content` JSON without updating child tables. See `references/anti-patterns.md`.
---
## Content JSON Format
The `content` field is a JSON array. Each element represents a block in the 12-column grid:
```json
[
{
"id": "unique-block-id",
"type": "header",
"data": {"text": "Overview", "level": 4, "col": 12}
},
{
"id": "unique-block-id-2",
"type": "chart",
"data": {
"chart_name": "Sales Trends",
"col": 12
}
},
{
"id": "unique-block-id-3",
"type": "number_card",
"data": {
"number_card_name": "Open Orders",
"col": 4
}
},
{
"id": "unique-block-id-4",
"type": "shortcut",
"data": {
"shortcut_name": "New Sales Order",
"col": 4
}
},
{
"id": "unique-block-id-5",
"type": "spacer",
"data": {"col": 12}
}
]
```
### Block Types
| Type | `data` fields | Description |
|------|---------------|-------------|
| `header` | `text`, `level`, `col` | Section heading (h3/h4/h5) |
| `chart` | `chart_name`, `col` | References a Dashboard Chart doc |
| `number_card` | `number_card_name`, `col` | References a Number Card doc |
| `shortcut` | `shortcut_name`, `col` | References a Workspace Shortcut child |
| `card` | `card_name`, `col` | Card break for grouped links |
| `quick_list` | `quick_list_name`, `col` | Recent records for a DocType |
| `custom_block` | `custom_block_name`, `col` | References a Custom HTML Block doc |
| `text` | `body`, `col` | Rich text / Markdown block |
| `spacer` | `col` | Empty vertical space |
| `onboarding` | `onboarding_name`, `col` | Module onboarding widget |
> `col` values MUST be 1-12 and represent grid column width. Blocks in the same row MUST sum to ≤ 12.
---
## Implementation Workflows
### Workflow 1: Create a Public Workspace via UI
1. Navigate to `/app/workspace` → click **+ New Workspace**
2. Set **Label** (appears in sidebar), **Module**, **Icon**
3. Use the Workspace Builder to drag-and-drop blocks
4. Add components: Charts, Number Cards, Shortcuts, Links
5. Click **Save** → workspace appears in sidebar for permitted users
6. In developer mode: JSON auto-exports to your app directory
### Workflow 2: Create a Workspace Programmatically
```python
import frappe
import json
workspace = frappe.new_doc("Workspace")
workspace.label = "Project Dashboard"
workspace.module = "Projects"
workspace.icon = "project"
workspace.type = "Workspace"
workspace.sequence_id = 10
# Build content blocks
workspace.content = json.dumps([
{
"id": frappe.generate_hash(length=10),
"type": "header",
"data": {"text": "Project Overview", "level": 4, "col": 12}
},
{
"id": frappe.generate_hash(length=10),
"type": "number_card",
"data": {"number_card_name": "Active Projects", "col": 4}
},
{
"id": frappe.generate_hash(length=10),
"type": "chart",
"data": {"chart_name": "Project Status", "col": 12}
}
])
# Add child table entries (MUST match content JSON)
workspace.append("number_cards", {
"number_card_name": "Active Projects"
})
workspace.append("charts", {
"chart_name": "Project Status"
})
# Role restrictions (optional)
workspace.append("roles", {"role": "Projects Manager"})
workspace.insert(ignore_permissions=True)
frappe.db.commit()
```
> **ALWAYS** add corresponding child-table rows when setting `content` JSON programmatically.
### Workflow 3: Create Supporting Documents First
Before adding components to a workspace, create the referenced documents:
**Number Card:**
```python
card = frappe.new_doc("Number Card")
card.label = "Active Projects"
card.document_type = "Project"
card.function = "Count"
card.filters_json = json.dumps([["Project", "status", "=", "Open"]])
card.is_public = 1
card.insert(ignore_permissions=True)
```
**Dashboard Chart:**
```python
chart = frappe.new_doc("Dashboard Chart")
chart.chart_name = "Project Status"
chart.chart_type = "Group By"
chart.document_type = "Project"
chart.group_by_type = "Count"
chart.group_by_based_on = "status"
chart.type = "Donut"
chart.is_public = 1
chart.insert(ignore_permissions=True)
```
**Shortcut:**
Shortcuts are child-table entries on the Workspace, not standalone docs:
```python
workspace.append("shortcuts", {
"label": "New Project",
"type": "DocType",
"link_to": "Project",
"color": "Blue",
"format": "{} Active",
"stats_filter": json.dumps([["Project", "status", "=", "Open"]])
})
```
See `references/workspace-components.md` for complete component reference.
---
## Permission Model
### Three Layers of Access Control
```
Layer 1: Module Access (PRIMARY)
└─► User must have access to the workspace's module
└─► Controlled via "Module Def" and user's "Block Modules" list
Layer 2: Role Restrictions (OPTIONAL)
└─► workspace.roles child table
└─► If populated: ONLY users with listed roles see the workspace
└─► If empty: ALL users with module access see it
Layer 3: Workspace Manager Role
└─► Required to create/edit PUBLIC workspaces
└─► NOT required for private workspaces
```
### Rules
- ALWAYS set `module` on public workspaces — without it, the workspace is visible to ALL Desk users
- ALWAYS add role restrictions for sensitive dashboards (financial, HR)
- NEVER set `for_user` on workspaces shipped with an app — it creates a private workspace
---
## Version Differences
| Feature | v14 | v15 | v16 |
|---------|-----|-----|-----|
| Workspace Builder UI | Basic | Redesigned (drag-drop grid) | Incremental fixes |
| `type` field (Workspace/Link/URL) | Not available | Added | Available |
| `app` field | Not available | Added | Available |
| `indicator_color` | Not available | Added | Available |
| Name collision protection | Manual | Manual | Auto-deduplicate |
| Welcome header config | Not available | Not available | Added |
| Content JSON format | Same | Same | Same |
### Migration Notes
- v14 → v15: Workspace Builder UI changed significantly; existing JSON content remains compatible
- v15 → v16: Minor field additions; no breaking changes to workspace structure
- ALWAYS test workspace rendering after major version upgrades
---
## Shipping Workspaces with a Custom App
### Directory Structure
```
myapp/
└── mymodule/
└── workspace/
└── my_workspace/
└── my_workspace.json
```
### Export Process
1. Enable **Developer Mode** (`frappe.conf.developer_mode = 1`)
2. Create/edit workspace via Workspace Builder UI
3. On save, Frappe auto-exports to the app directory above
4. Commit the JSON file to version control
### CRITICAL: Ship Dependencies Too
A workspace JSON alone is NOT sufficient. You MUST also ship:
| Component | How to Ship |
|-----------|-------------|
| Number Cards | fixtures in hooks.py OR `myapp/fixtures/` |
| Dashboard Charts | fixtures in hooks.py OR `myapp/fixtures/` |
| Custom HTML Blocks | fixtures in hooks.py OR `myapp/fixtures/` |
| Linked Reports | Already shipped via report directory structure |
| Linked Pages | Already shipped via page directory structure |
```python
# hooks.py
fixtures = [
{"dt": "Number Card", "filters": [["module", "=", "My Module"]]},
{"dt": "Dashboard Chart", "filters": [["module", "=", "My Module"]]},
{"dt": "Custom HTML Block", "filters": [["name", "in", ["My Block"]]]},
]
```
See `references/shipping-with-app.md` for complete shipping guide.
---
## Common Patterns
### Pattern 1: Module Dashboard with KPIs
```
[Header: "Key Metrics"]
[Number Card: Open Orders (col=3)] [Number Card: Revenue (col=3)]
[Number Card: Pending (col=3)] [Number Card: Overdue (col=3)]
[Spacer]
[Header: "Trends"]
[Chart: Monthly Revenue (col=12)]
[Header: "Quick Access"]
[Shortcut: New Order (col=4)] [Shortcut: Reports (col=4)] [Shortcut: Settings (col=4)]
```
### Pattern 2: Role-Based Workspace
```python
# Sales Manager sees full dashboard; Sales User sees limited view
# Option A: Two separate workspaces with different role restrictions
# Option B: One workspace — use Number Card/Chart permissions to filter
# Option A implementation:
ws_manager = frappe.get_doc({"doctype": "Workspace", "label": "Sales Management", ...})
ws_manager.append("roles", {"role": "Sales Manager"})
ws_user = frappe.get_doc({"doctype": "Workspace", "label": "Sales Overview", ...})
ws_user.append("roles", {"role": "Sales User"})
```
### Pattern 3: Sidebar Hierarchy
```python
# Parent workspace
parent = frappe.get_doc({"doctype": "Workspace", "label": "CRM", "module": "CRM"})
# Child workspaces (nested in sidebar)
child = frappe.get_doc({
"doctype": "Workspace",
"label": "Lead Pipeline",
"module": "CRM",
"parent_page": "CRM" # References parent workspace label
})
```
---
## Reference Files
| File | Content |
|------|---------|
| `references/workspace-components.md` | Number Cards, Dashboard Charts, Shortcuts, Custom Blocks — full API |
| `references/shipping-with-app.md` | JSON format, fixtures, module structure, install hooks |
| `references/anti-patterns.md` | Common workspace mistakes and how to avoid them |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!