dashboard-layout
Dashboard layout with dashboard_write ops — the 24-column grid, per-display default sizes, explicit position vs autoplace, the KPI-row pattern, headings and text cards, tabs and the every-card-needs-a-tab rule. Triggers — "build a dashboard from these cards", "the dashboard is squished into half the width", "arrange the cards", "add a tab", "make a KPI row".Dashboard layout
Ordered dashboard_write ops apply as one atomic save — batch a whole rearrangement in one ops list. New dashcards and tabs get negative ids you choose (-1, -2, …), referenced by later ops in the same call; the server assigns real ids on save. validate_only: true returns the resulting layout without writing.
The grid is 24 columns — not 12
A dashcard is {row, col, size_x, size_y}: col 0-indexed, row grows downward, col + size_x ≤ 24. Full width is size_x: 24. A layout authored on a 12-column assumption crams everything into the left half — if no card crosses column 12, double every width.
Omit position to autoplace (below/beside existing cards) and size for the display's default — right when appending a card or two. Compose whole layouts with explicit position + size; place a band wholly explicitly or let it all autoplace, never mixed. Sanity-check: rows fill to 24, at least one card ends at col + size_x = 24.
Default sizes (w×h): scalar/smartscalar 6×3 · pie 12×8 · table/pivot 12×9 · waterfall 14×6 · sankey 16×10 · iframe 12×8 · heading 24×1 · text 12×3 · link 8×1 · action 4×1 · other charts 12×6.
The standard shape: KPI row, chart halves, full-width table
dashboard_write {"method": "create", "name": "Revenue overview",
"ops": [
{"op": "add_heading", "id": -1, "text": "Key numbers", "position": {"row": 0, "col": 0}},
{"op": "add_card", "id": -2, "card_id": 101, "position": {"row": 1, "col": 0}, "size": {"size_x": 6, "size_y": 3}},
{"op": "add_card", "id": -3, "card_id": 102, "position": {"row": 1, "col": 6}, "size": {"size_x": 6, "size_y": 3}},
{"op": "add_card", "id": -4, "card_id": 103, "position": {"row": 1, "col": 12}, "size": {"size_x": 6, "size_y": 3}},
{"op": "add_card", "id": -5, "card_id": 104, "position": {"row": 1, "col": 18}, "size": {"size_x": 6, "size_y": 3}},
{"op": "add_card", "id": -6, "card_id": 105, "position": {"row": 4, "col": 0}, "size": {"size_x": 12, "size_y": 6}},
{"op": "add_card", "id": -7, "card_id": 106, "position": {"row": 4, "col": 12}, "size": {"size_x": 12, "size_y": 6}},
{"op": "add_card", "id": -8, "card_id": 107, "position": {"row": 10, "col": 0}, "size": {"size_x": 24, "size_y": 9}}]}
Structure and prose
add_heading— full-width section label (plain text, 24×1); one per band.add_text— markdown card for explanations and captions.add_link(URL or Metabase entity),add_iframe— external content.add_action— button running a saved action (action_id; optionallabel,display:"button"/"form").duplicate_cardcopies a dashcard with its settings;replace_cardswaps the card behind a slot, keeping the slot.
Editing an existing layout
move (new position and/or tab), resize (size), remove, addressed by dashcard id from get_content. Layout keys never ride patch_dashcard (content merges only) — move/resize/replace_card/set_series own row, col, size, tab, card_id, series.
Tabs
"ops": [{"op": "add_tab", "id": -1, "name": "Overview"},
{"op": "add_tab", "id": -2, "name": "Details"},
{"op": "add_card", "id": -3, "card_id": 101, "tab": -1},
{"op": "move", "dashcard_id": 7, "tab": -2}]
- Existing cards are adopted automatically only when the save ends with exactly one tab; adding two or more in one call leaves them orphaned —
movethem onto a tab in the same call. - With more than one tab, every card must have a tab (
tabon each add op, ormove); the save rejects orphans by id. rename_tab,move_tab(reorder),duplicate_tab(copies the tab and its cards),remove_tab(deletes its cards too).- A header filter appears on a tab only where it is wired to a card on that tab —
learn("dashboard-filters").
Don't
- Don't author 12-column widths — the dashboard fills half the screen, with no error.
- Don't mix explicit positions and autoplace within one band — the band misaligns.
- Don't spread a rearrangement over several calls — each autoplaces against a half-built layout.
To confirm
get_content {"type": "dashboard", "id": 40, "include": ["layout"]} returns every dashcard's tab, position, and size — check bands fill to 24 and every card has a tab. validate_only: true previews the same without saving.
Compétences associées
Découvrez d'autres compétences du même dépôt source.
Documents A document is a rich-text page mixing prose with embedded saved questions. takes **Metabase-flavored Markdown**: CommonMark plus tokens. Charts must exist first ( ), embedded by id. **CommonMark subset:** headings, paragraphs, bold/italic/inline code, links (bare URLs...