Skip to Content
WidgetWhat It Reports

What validateItems reports

@evanion/widget 0.1.0 and its patch releases up to 0.1.3, the version on npm. Values on this page were produced by running the source at f9b1d93, a later commit than the 0.1.0 tag, and are not re-executed.

Even with a strictly typed renderer, you still need a way to validate the data arriving from your CMS. A single typo in a widget type or a missing required prop can lead to a “broken” page where parts of the UI simply vanish.

validateItems(items, known) allows you to scan a whole payload in one pass, identifying every fault—including those nested deep within other widgets—without throwing exceptions.

The Problem: Structural Faults in CMS Data

CMS data is inherently untyped. When your frontend receives a JSON payload, a few common errors can cause the UI to fail silently:

  • Unknown Types: A content editor types listting instead of listing.
  • Missing Props: A required field like title is left blank.
  • Malformed Structure: An item is a string instead of an object, or children is not a list.
  • Identity Collisions: Two widgets in the same section share the same id.

If these aren’t caught, your renderer will simply skip the items it cannot draw and draw the rest with whatever data they carry, leaving mysterious gaps in your page.

Solution: Deep-Payload Scanning

validateItems performs a depth-first walk of your widget tree. It reports an item’s own problems first, then recursively validates its children before moving to the next sibling.

Consider a payload with three distinct faults: a missing props object, a duplicate ID, and a typo in a widget type.

import { } from '@evanion/widget'; const = [ { : 'wingspan', : 'listing' }, { : 'wingspan', : 'listing', : { : 'Wingspan' } }, { : 'tonight', : 'shelf', : {}, : [{ : 'hive', : 'listting', : { : 'Hive' } }], }, ]; const = (, ['listing', 'shelf']).( ({ , }) => ({ , }), ); ; // -> [{ index: 0, message: 'props is not an object' }, { index: 1, message: 'duplicate sibling id' }, { index: 0, message: 'unknown widget type' }]

In this case, the engine identifies the missing props on the first item and the duplicate ID on the second. Finally, it descends into the tonight shelf and finds that its child, Hive’s listing, has an unknown type.

Because it walks the tree depth-first, Hive’s problem is reported last, and its index is 0 because it is the first child of the shelf, not the third item in the overall payload.

Anatomy of a WidgetProblem

Every fault found by the library is returned as a WidgetProblem object:

FieldDescription
indexThe item’s position among its siblings. Returns -1 if the payload is not a list.
idThe item’s id string, or - if the ID is missing.
typeThe item’s type string, or - if the type is missing.
messageA human-readable error string from VALIDATION_MESSAGES.

Standardized Error Messages for Automation

To make it easier to translate or group errors, @evanion/widget exports VALIDATION_MESSAGES. Instead of comparing raw strings, you should compare the message against these constants.

ConstantMessageCause
NOT_A_LISTitems is not a listThe payload is not an array.
NOT_AN_OBJECTitem is not an objectAn entry is a primitive (string, number, etc.) instead of an object.
INVALID_IDitem id is not a stringThe id field is missing or is not a string.
INVALID_TYPEitem type is not a stringThe type field is missing or is not a string.
UNKNOWN_TYPEunknown widget typeThe type is not present in the known list.
INVALID_PROPSprops is not an objectThe props field is missing, or is not an object.
INVALID_CHILDRENchildren is not a listThe children field is present but is not an array.
DUPLICATE_IDduplicate sibling idTwo items in the same sibling list share the same id.
MISSING_FIELDmissing field <name>A field marked as required is absent or contains only whitespace.

Common Pitfall: Placing data at the top level of the item (e.g., { id: '1', type: 'listing', title: 'Game' }). This will trigger an INVALID_PROPS error because the renderer only looks for data inside the props object.

Enforcing Required Fields in CMS Data

Beyond checking if a widget type is known, you can enforce that certain props are present and not blank (where “blank” means undefined, null, or a string of only whitespace).

You do this by passing a required map as the third argument to validateItems:

import { } from '@evanion/widget'; const = ( [{ : 'wingspan', : 'listing', : { : ' ' } }], ['listing'], { : ['title'] }, ); ; // -> [{ index: 0, id: 'wingspan', type: 'listing', message: 'missing field title' }]

In the example above, even though title is a string, it is blank. validateItems reports it as missing field title, so your gate can stop the page before your renderer draws a listing without a name.

Note that required checks only happen for known types. If a widget’s type is unknown, it is reported as an UNKNOWN_TYPE fault, and its required props are ignored.

Syncing Validation with Your Renderer

You can pass the same registry your renderer uses as the known argument to validateItems. This ensures that your validation logic and your rendering logic always agree on which types exist.

If you are running a validation check in a CI environment where you cannot import your React components, you can simply pass a plain array of type names.

import { , } from '@evanion/widget'; // Whatever your renderer resolves a type to. The core reads the keys and never // calls a value, so a stand-in is enough to show what the keys do. const = () => null; const = () => null; const = ({ : , : }); const = ( [{ : 'wingspan', : 'listing', : { : 'Wingspan' } }], , ); ; // -> []

By using defineWidgets, you get the best of both worlds: a typed registry for your code editor and a plain list of strings for validateItems.

Next steps: Layout and Rendering

Now that you can identify faults in your content, you can learn how the renderer actually handles the valid items. Order and nesting explains how the library handles layout, nesting, and placement.

Last updated on