Skip to Content
React WidgetOverview

Ahead of the release

npm install @evanion/react-widget gives you 0.3.0. These pages document main, which has changes that release does not.

@evanion/react-widget

Render a region of React components from structured data — a shop page, a back-office view, a configurable shelf — with the data checked against the components at compile time. Baize’s shelf below is two widget types and the items the shop’s data gives them.

import { } from '@evanion/react-widget'; const = ({ , }: { : string; : number }) => ( < ="listing"> <>{}</> <>{} kr</> </> ); const = ({ , , }: { : number; : boolean; }) => ( < ="booking"> <>{} tables</> <>{ ? 'tonight' : 'this week'}</> </> ); // Call once, at module scope. const { , } = ({ : { : , : }, : { : ({ }) => < ="shelf">{}</>, }, }); export function () { return ( < ={[ { : 'b1', : 'booking', : { : 4, : true } }, { : 'g1', : 'listing', : { : 'Brass: Birmingham', : 649 }, }, ]} /> ); }

The component map drives everything. An unknown type, a missing or mistyped prop, or children on a component that does not accept them is a compile error rather than a warning in a console somebody has to be watching.

Server components

The package carries no 'use client'. It uses only what React exports under the react-server condition — createElement, Suspense, memo — and no createContext, useContext, Component or stateful hook. A page that renders <Widgets> can be a Server Component, and a widget can be an async Server Component that fetches its own data.

There is no provider, no hook and no class error boundary here, because react/package.json maps the react-server condition to a build that does not export the APIs those need. What would have been context is a prop: ctx for page-level data, meta for placement.

What it gives you

PieceWhat it does
createWidgetsbuilds a widget set from a component map; call once at module scope
Widgetsrenders a list of items
defineItemssupplies the contextual type for an item array held in a variable
validateItemschecks data that never met the type checker, at ingestion or in CI
chromewrapper, per-item wrapper, Suspense boundary and fallback
metaplacement data that reaches the item chrome and never the widget
ctxpage-level data passed to every widget as a prop

Where to go next

@evanion/astro-widget is the same idea for Astro: a registry instead of a component map, ctx instead of a provider, and no recursion into children because an Astro component receives child content through <slot />.

@evanion/compose handles the neighbouring problem of nesting providers, for the parts of an app that do have context.

Last updated on