Skip to Content
React WidgetChecking a CMS Payload

Checking a CMS payload

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

validateItems checks a payload that the TypeScript compiler never saw—such as a JSON page exported from a CMS—and returns every structural problem found. This allows you to catch “broken” content in a CI script or a publish webhook before it reaches your users.

The Problem: The “Untyped Payload” Gap

While defineItems ensures your code is type-safe, the data coming from your CMS is just plain JSON. A content editor might accidentally use a widget type that no longer exists in your code, or leave a required field blank.

If you pass this raw JSON directly to <Widgets>, the renderer will simply skip items of an unknown type and draw items with blank fields as they are. In production, this results in a “silent failure”—a gap in your page that you only discover after the page is live.

Solution: Runtime Validation in CI/CD

@evanion/react-widget re-exports validateItems from @evanion/widget. You can use this function in a build script or a webhook handler to block the publishing of broken content.

import { } from '@evanion/react-widget'; const : unknown = .( '[{"id":"root","type":"listing","props":{"title":"Root"}},{"id":"raffle","type":"raffle","props":{}}]', ); const = (, ['listing', 'booking']); // -> [{ index: 1, id: 'raffle', type: 'raffle', message: 'unknown widget type' }]

How the Validation Works:

  • Payload: The parsed JSON from your CMS, typed as unknown.
  • Known Types: The list of widget types your renderer actually supports.
  • Required Props: (Optional) A map that defines which props are mandatory for each widget type.

validateItems returns an array of WidgetProblem objects. If the list is not empty, your CI script can set process.exitCode = 1 to fail the build, or a webhook can send an error message back to the CMS editor.

Enforcing Mandatory Props

You can ensure that a widget is not just “known” but also “complete.” By passing a required map, you can reject items that have missing or blank props (where “blank” means undefined, null, or a string of only whitespace).

const { } = ({ : { : }, }); const : unknown = [ { : 'root', : 'listing', : { : ' ' } }, ]; const = (, { : ['title'] }); // -> [{ index: 0, id: 'root', type: 'listing', message: 'missing field title' }]

validateItems reports when essential data—like a listing’s title—is missing, before the renderer even tries to draw the component.

Handling Nested Problems

Because widgets can contain other widgets, validateItems recursively scans the children array of every item. If a nested child is faulty, the WidgetProblem reports its index among its own siblings.

import { } from '@evanion/react-widget'; const : unknown = .( '[{"id":"tonight","type":"showcase","props":{"title":"On the table tonight"},"children":[{"id":"hive","type":"listing","props":{"title":"Hive"}},{"id":"raffle","type":"raffle","props":{}}]}]', ); const = (, ['showcase', 'listing']); // -> [{ index: 1, id: 'raffle', type: 'raffle', message: 'unknown widget type' }]

To find the exact location of a nested error, you can search the children of your items for the reported id.

Rendering a Validated Payload

Once a payload has passed validation, you can safely cast it to the expected item type and render it.

import type { } from '@evanion/react-widget'; const = { : }; const { , } = ({ }); function (: unknown): <typeof >[] { const = (, { : ['title'] }); return . === 0 ? ( as <typeof >[]) : []; } const : unknown = .( '[{"id":"root","type":"listing","props":{"title":"Root"}}]', ); const : unknown = .( '[{"id":"root","type":"listing","props":{"title":" "}}]', ); const = (< ={()} />); // -> '<section><div data-widget-id="root" data-widget-type="listing"><h3>Root</h3></div></section>' const = (< ={()} />); // -> '<section></section>'

Important: validateItems checks for the existence of props, not their type. If a title is required and the CMS sends a number instead of a string, validateItems will pass it. If you need strict type validation for your JSON, consider using a schema validator like Zod alongside validateItems.

What Happens to Unvalidated Items?

If you skip the validation step and pass a raw payload to <Widgets>, the renderer follows a “fail-soft” strategy:

  1. Skip: Any item with an unknown type is simply not rendered.
  2. Warn: In development (NODE_ENV !== 'production'), the renderer logs a warning to the console: Unknown widget type "..." for widget ID "...". Skipping render.
  3. Silence: In production, these warnings are suppressed. A stale widget type will leave a silent gap in your page.

This is why running validateItems in your CI/CD pipeline is the only way to guarantee that every item in your CMS is actually rendered on the page.

Last updated on