Skip to Content
Astro WidgetOverview

@evanion/astro-widget

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

Render CMS-driven pages with zero-JS overhead using Astro.

A content management system (CMS) saves a page as a list of items. To render this page, you need to map each item’s type to a specific .astro component and pass it the corresponding props.

@evanion/astro-widget provides the Widgets.astro component, which automates this process during Astro’s build phase. Because the rendering happens entirely on the server, it sends no JavaScript to the browser, ensuring maximum performance and an instant first paint.

The Problem: The “Static Content” Bottleneck

In many Astro projects, developers manually place components on a page. However, when a page is driven by a CMS, you don’t know which components are needed until the data is fetched.

Manually writing a switch statement to render components based on a type string is repetitive and error-prone. Furthermore, since .astro components don’t have the same prop-inference capabilities as React components in a TypeScript environment, it’s easy to miss a required prop in the CMS data, leading to broken pages.

Solution: Data-Driven Component Mapping

@evanion/astro-widget solves this by using a registry. You define a map where the keys are your widget types (e.g., 'listing-header') and the values are the corresponding .astro components.

The Widgets.astro component then takes your item array and the registry, rendering each item in order through its mapped component.

import { experimental_AstroContainer as AstroContainer } from 'astro/container'; import Widgets from '@evanion/astro-widget/components/Widgets.astro'; import { registry } from '../examples/src/registry'; const items = [ { id: 'header', type: 'listing-header', props: { title: 'Brass: Birmingham', players: '2-4' }, }, { id: 'price', type: 'price-box', props: { price: '649 kr' } }, ]; const container = await AstroContainer.create(); const html = await container.renderToString(Widgets, { props: { items, registry }, }); html; // -> '<header><h1>Brass: Birmingham</h1><p>2-4 players</p></header><p class="price">649 kr</p>'

By decoupling the content (the item array) from the rendering logic (the registry), you can change the entire layout of a page simply by updating a JSON file in your CMS.

The astro-widget Vocabulary

These terms are used throughout the documentation:

  • Item: A single entry in the CMS list. It contains an id, a type, and the props for its component. It can also carry meta (placement data) and children (nested items).
  • Widget: The .astro component that renders an item.
  • Registry: The map linking each type to its widget. defineWidgets is used to build a type-safe registry.
  • Problem: A fault identified by validateItems, such as a type that isn’t in the registry.

Note: The core logic for items, the registry, and validation is provided by @evanion/widget. astro-widget re-exports these tools so you can maintain a single source of truth for your content validation.

Core Capabilities

You want to…Use this toolTaught on
Render a page from CMS itemsWidgets.astroGetting started
Get autocomplete for type namesdefineWidgetsGetting started
Fail the build on broken contentvalidateItemsCheck CMS items
Pass page-level data to widgetsThe ctx propLayout
Wrap widgets in shared markupchrome.itemLayout
Render nested widgetsRecursive Widgets usageLayout

When astro-widget is the Wrong Tool

  • Dynamic Content: If a page needs to change after it has loaded in the browser, use @evanion/react-widget within an Astro island.
  • Strict Prop Checking: If you need the compiler to verify every prop of every item, use @evanion/react-widget. Astro components do not support the same level of prop inference.
  • Static Pages: If your page structure never changes and is not driven by a CMS, simply write the components directly into your .astro page.

Build the Baize Listing Page

You’ve seen how Widgets.astro turns a few items into a header and a price box. Getting started guides you through installing the package, creating your first widgets, and rendering a full listing page from a CMS JSON file.

Last updated on