These pages document @evanion/astro-widget 0.3.4, the version on npm.
What a bad CMS save reports and renders
validateItems reports every fault in a list of items, and Widgets.astro renders what it can of the same list. This guide examines the relationship between a validation error and its resulting effect on the rendered page.
Faults in an Item’s Shape
Alongside checking for a specific widget type, validateItems checks if an item is structurally sound. If a CMS save contains broken entries, the engine identifies the faults based on six structural rules.
import { } from '@evanion/astro-widget';
const = [
null,
{ : 'listing-header', : { : 'Brass: Birmingham' } },
{ : 'grid', : 'game-grid', : {}, : 'none' },
{ : 'grid', : 'game-grid' },
];
const = (, ['listing-header', 'game-grid']);
.(() => [., .]); // -> [[0, 'item is not an object'], [1, 'item id is not a string'], [2, 'children is not a list'], [3, 'duplicate sibling id'], [3, 'props is not an object']]| Entry | Structural Fault | Resulting Message |
|---|---|---|
| 0 | Saved as null instead of an object | item is not an object |
| 1 | Missing the id field | item id is not a string |
| 2 | children is a string instead of a list | children is not a list |
| 3 | id repeats a sibling’s id | duplicate sibling id |
| 3 | Missing the props object | props is not an object |
Special Case: Payload is not a list
If the entire payload is not an array (e.g., it’s an object { items: [] }), validateItems returns a single problem with an index of -1 and a message of items is not a list.
import { } from '@evanion/astro-widget';
({ : [] }, ['listing-header']); // -> [{ index: -1, id: '-', type: '-', message: 'items is not a list' }]Nested Indexing
Because validateItems performs a depth-first walk, it reports problems for nested children using an index relative to their own siblings. A top-level item and its first child can both report index: 0. Use the id field to identify the faulty item; it tells the two apart only when your CMS keeps ids unique across the whole tree.
import { } from '@evanion/astro-widget';
const = [
{
: 'grid',
: 'game-grid',
: {},
: [{ : 'questions', : 'answer-wall', : {} }],
},
];
(, ['game-grid'], { 'game-grid': ['title'] }); // -> [{ index: 0, id: 'grid', type: 'game-grid', message: 'missing field title' }, { index: 0, id: 'questions', type: 'answer-wall', message: 'unknown widget type' }]How Widgets.astro Renders a Bad Item
Widgets.astro follows a “fail-soft” strategy: it renders every item it can and throws on none. This means a bad save publishes with a gap in the UI.
Unless NODE_ENV is production, the renderer prints a warning to the console using ERROR_MESSAGES.
| Fault | Render Result | Console Warning |
|---|---|---|
Unknown type | Nothing renders | ERROR_MESSAGES.UNKNOWN_WIDGET |
No string type | Nothing renders | ERROR_MESSAGES.UNKNOWN_WIDGET (type: “unknown”) |
| Not an object | Nothing renders | ERROR_MESSAGES.UNKNOWN_WIDGET (type/id: “unknown”) |
| Missing prop | Widget renders, but with undefined for that prop | None |
items is not a list | Nothing renders | ERROR_MESSAGES.MALFORMED_ITEMS |
Missing/Repeated id | Widget renders (if type is known) | None |
children not a list | Widget renders; its internal <Widgets /> warns | ERROR_MESSAGES.MALFORMED_ITEMS |
The “Double-Validation” Gap
A common point of confusion is when an item passes the global validateItems check but still renders incorrectly. This usually happens with nested registries.
Imagine a GameGrid widget that only renders game-card types. If the global build gate checks the page with one known list, it might pass a listing-header inside a grid. However, when GameGrid tries to render its children using its own cardRegistry, the listing-header is unknown and will be skipped.
import { , type } from '@evanion/astro-widget';
const : [] = [
{
: 'header',
: 'listing-header',
: { : 'Brass: Birmingham' },
},
{
: 'grid',
: 'game-grid',
: {},
: [
{ : 'azul', : 'listing-header', : { : 'Azul' } },
],
},
];
const = { 'listing-header': ['title'] };
const = .(() => . === 'game-grid');
(, ['listing-header', 'game-grid'], ); // -> []
.(() => (. ?? [], ['game-card'])); // -> [{ index: 0, id: 'azul', type: 'listing-header', message: 'unknown widget type' }]To prevent this, you should run validateItems against the specific registries used by your container widgets.
Interactive Comparison
You can compare how different faults are reported and rendered using the demo below. You can pick various “bad” lists and see the resulting WidgetProblem array and the final HTML output.
items
[
null,
{
"type": "listing-header",
"props": {
"title": "Brass: Birmingham"
}
},
{
"id": "grid",
"type": "game-grid",
"props": {},
"children": "none"
},
{
"id": "grid",
"type": "game-grid"
}
]known
[
"listing-header",
"game-grid"
]required
not passedvalidateItems(items, known)
| index | id | type | message |
|---|---|---|---|
| 0 | - | - | item is not an object |
| 1 | - | listing-header | item id is not a string |
| 2 | grid | game-grid | children is not a list |
| 3 | grid | game-grid | duplicate sibling id |
| 3 | grid | game-grid | props is not an object |
<Widgets items={items} registry={registry} />
A registry holding the widget under examples/src/widgets for each name in known.
<header data-astro-source-file="/home/runner/work/libraries/libraries/libs/astro-widget/examples/src/widgets/ListingHeader.astro" data-astro-source-loc="11:9"><h1 data-astro-source-file="/home/runner/work/libraries/libraries/libs/astro-widget/examples/src/widgets/ListingHeader.astro" data-astro-source-loc="12:7">Brass: Birmingham</h1><p data-astro-source-file="/home/runner/work/libraries/libraries/libs/astro-widget/examples/src/widgets/ListingHeader.astro" data-astro-source-loc="13:6"> players</p></header><section data-astro-source-file="/home/runner/work/libraries/libraries/libs/astro-widget/examples/src/widgets/GameGrid.astro" data-astro-source-loc="16:10"><h2 data-astro-source-file="/home/runner/work/libraries/libraries/libs/astro-widget/examples/src/widgets/GameGrid.astro" data-astro-source-loc="17:7"></h2></section><section data-astro-source-file="/home/runner/work/libraries/libraries/libs/astro-widget/examples/src/widgets/GameGrid.astro" data-astro-source-loc="16:10"><h2 data-astro-source-file="/home/runner/work/libraries/libraries/libs/astro-widget/examples/src/widgets/GameGrid.astro" data-astro-source-loc="17:7"></h2></section>console.warn under astro dev
Unknown widget type "unknown" for widget ID "unknown". Skipping render.Malformed `items` prop: expected an array of widget items. Skipping render.
For a complete list of all possible validation messages, visit the What it reports page in the core documentation.