These pages document @evanion/widget 0.1.3, the version on npm.
Order and nesting
A renderer draws widget items in array order, and each item’s children appear inside it. This means that any edit made to the order of items in your content management system (CMS) is reflected immediately on the page—and everything nested within a widget moves with it.
The Problem: Complex, Nested Page Layouts
Modern pages aren’t just flat lists of components. They often feature nested structures: a “Featured Shelf” that contains “Game Listings,” or a “Dashboard Row” that contains “Metric Cards.”
Manually managing these relationships in code is fragile. If you want to move a whole section of the page, you have to carefully move the parent and all its children, ensuring you don’t break the nesting logic.
Solution: Hierarchical Widget Items
@evanion/widget models the page as a tree. By utilizing the children property on a widget item, you can create deeply nested layouts that are easy to move and reorganize.
Consider a staff dashboard:
import { } from '@evanion/widget';
import type { } from '@evanion/widget';
const : [] = [
{
: 'week',
: 'columns',
: {},
: [
{
: 'intake',
: 'metric',
: { : '38', : 'Games in', : '+6' },
},
{
: 'sold',
: 'metric',
: { : '31', : 'Sold', : '+2' },
},
{
: 'turnaround',
: 'metric',
: { : '2.4 d', : 'Turnaround', : '−0.3' },
},
],
},
{
: 'counter',
: 'columns',
: {},
: [
{
: 'tables',
: 'tables',
: { : 'Tonight at the tables' },
: { : 2 },
},
{
: 'reprints',
: 'reprints',
: { : 'Reprints on order' },
},
],
},
];
const = (, [
'columns',
'metric',
'tables',
'reprints',
]);
; // -> []In this structure:
- The root contains two main sections:
weekandcounter. - The
weekitem acts as a container, wrapping threemetricwidgets. - The
counteritem wraps two board widgets:tablesandreprints.
Because the children are nested inside the parent item, moving the week item to the bottom of the array moves all three metrics with it, preserving the group’s integrity.
Placement and the Item Chrome
While the widget’s props contain the data, the widget’s meta property contains placement data.
The item chrome (chrome.item) is the component that surrounds each widget. It uses the meta field to determine how the widget should be positioned (e.g., in a grid).
In the dashboard example, the tables widget has meta: { span: 2 }. The item chrome sees this and assigns the widget two grid columns. If you move the tables widget to a different position in the array, its span: 2 metadata moves with it, ensuring the layout remains consistent regardless of where the widget sits on the page.
Renderer Differences: React vs. Astro
While @evanion/react-widget and @evanion/astro-widget both use the same core widget items and follow the same order, they handle the rendering process differently:
1. Handling Children
@evanion/react-widget: Draws each child first and passes the resulting React elements as thechildrenprop to the parent component.@evanion/astro-widget: Passes the raw child items to the parent component. The parent is then responsible for rendering its own<Widgets />component over those items.
2. The Item Chrome
- React: Uses a default item chrome (a
div) that ignoresmetaunless you provide a custom one. - Astro: Does not provide a default item chrome. If you pass one, it must render a
<slot />, or the widget inside will not appear on the page.
Move a Widget Item and Watch the Page Follow
@evanion/react-widget draws the dashboard below from the seven items above, and draws it again after every move. The items are printed under the page, and each line that opens an item has two move controls beside it.
- Move
counteraboveweek. The two rows swap, and each row keeps its own items, because an item’schildrenmove with it. - Move the board
tablespast the boardreprints. The wide board changes sides, becausemetabelongs to the item it sits on, so the width moves withtables.
Tonight at the tables
| Table | Game | Party | State |
|---|---|---|---|
| One | Brass: Birmingham | Hanna Lind, +3 | Playing |
| Two | Spirit Island | Otto Ruane, +2 | Playing |
| Three | Root | Sigrid Vall, +3 | Teaching |
| Four | Crokinole | Emil Norrby, +1 | Booked, 20:00 |
Reprints on order
| Title | Due |
|---|---|
| Wingspan, Oceania | Tuesday |
| Hive Pocket | Thursday |
| Azul, Summer Pavilion | Next week |
itemsMove one and the page is composed again
[ { "id": "week", "type": "columns", "props": {}, "children": [ { "id": "intake", "type": "metric", "props": { "figure": "38", "label": "Games in", "delta": "+6" } }, { "id": "sold", "type": "metric", "props": { "figure": "31", "label": "Sold", "delta": "+2" } }, { "id": "turnaround", "type": "metric", "props": { "figure": "2.4 d", "label": "Turnaround", "delta": "−0.3" } } ] }, { "id": "counter", "type": "columns", "props": {}, "children": [ { "id": "tables", "type": "tables", "props": { "title": "Tonight at the tables" }, "meta": { "span": 2 } }, { "id": "reprints", "type": "reprints", "props": { "title": "Reprints on order" } } ] }]Build a Custom Renderer for Layout
The dashboard on this page is powered by @evanion/react-widget. However, if you are using a framework without a published renderer, you can build your own. Your own renderer shows how to implement the logic to draw items in order and handle nesting.