Skip to Content
Astro WidgetBad Items

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']]
EntryStructural FaultResulting Message
0Saved as null instead of an objectitem is not an object
1Missing the id fielditem id is not a string
2children is a string instead of a listchildren is not a list
3id repeats a sibling’s idduplicate sibling id
3Missing the props objectprops 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.

FaultRender ResultConsole Warning
Unknown typeNothing rendersERROR_MESSAGES.UNKNOWN_WIDGET
No string typeNothing rendersERROR_MESSAGES.UNKNOWN_WIDGET (type: “unknown”)
Not an objectNothing rendersERROR_MESSAGES.UNKNOWN_WIDGET (type/id: “unknown”)
Missing propWidget renders, but with undefined for that propNone
items is not a listNothing rendersERROR_MESSAGES.MALFORMED_ITEMS
Missing/Repeated idWidget renders (if type is known)None
children not a listWidget renders; its internal <Widgets /> warnsERROR_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.

List

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 passed

validateItems(items, known)

indexidtypemessage
0--item is not an object
1-listing-headeritem id is not a string
2gridgame-gridchildren is not a list
3gridgame-gridduplicate sibling id
3gridgame-gridprops 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.
Five structural faults. validateItems returned item is not an object at index 0, item id is not a string at index 1, children is not a list at index 2, duplicate sibling id at index 3, props is not an object at index 3. Widgets.astro rendered <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>.

For a complete list of all possible validation messages, visit the What it reports page in the core documentation.

Last updated on