Getting started
@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.
In this guide, you will build a component-driven page using @evanion/react-widget. You’ll learn how to map CMS-style data to React components and how to ensure that your items are type-safe, preventing runtime crashes when a component receives the wrong data.
Prerequisites
To follow this guide, ensure you have the following:
- Node 20 or newer.
- React 18 or 19: Along with
react-domat the same version. - An ES module project: Your
package.jsonmust include"type": "module". - TypeScript 5.0 or newer: Ensure your
tsconfig.jsonhas"jsx": "react-jsx"undercompilerOptionsto allow JSX without importing React.
To set up a fresh project for this example, run:
npm init -y
npm pkg set type=module
npm install --save-dev typescript tsx @types/react @types/react-dom
npx tsc --init --jsx react-jsxInstall react-widget
Run the command for your preferred package manager (this will also install React and react-dom):
npm install @evanion/react-widget react react-domRender your first widget region
The primary entry point is createWidgets. It takes a component map (linking widget types to React components) and returns a Widgets component and a defineItems helper.
The “Type-Safe Array” Problem
Because TypeScript normally widens string types in arrays, passing a variable to <Widgets> would normally result in a compile error. defineItems solves this by typing the array against your component map, ensuring every type is a valid key and every props object matches the component’s requirements.
Here is how to render a Baize listing to HTML using renderToStaticMarkup:
const = (: { : string; : number }) => (
<>
{.}, {.} kr
</>
);
// 1. Create a typed widget set from the component map
const { , } = ({
: { : },
});
// 2. Define items with compile-time prop checking
const = ([
{
: 'brass',
: 'listing',
: { : 'Brass: Birmingham', : 649 },
},
]);
// 3. Render the region
const = (< ={} />); // -> '<section><div data-widget-id="brass" data-widget-type="listing"><h3>Brass: Birmingham, 649 kr</h3></div></section>'How to run the example:
- Create
render.tsxand importcreateWidgetsandrenderToStaticMarkup. - Paste the code above and add
console.log(html);at the end. - Run
npx tsx render.tsx.
The output will show a <section> (the default chrome.wrapper) containing a <div> (the default chrome.item) which wraps the actual ListingCard markup.
Verifying Type Safety
While tsx runs the code immediately, you can verify the type safety by running npx tsc --noEmit. If you change a price from a number to a string in your items, TypeScript will report a type mismatch, preventing a potential runtime error in your component.
Important: Call createWidgets once at the module scope. Calling it inside a component will cause the entire region to remount on every render, destroying the internal state of your widgets.
Expanding your layout
You now have a single region rendering a list of items. In a real application, you often need to group these items—for example, putting a set of listings inside a “Featured” showcase.
In Nesting items, we’ll explore how to create widgets that can contain other widgets.