Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
ee05251
add version number graphic to welcome banner
retrofox May 24, 2026
682417f
Merge branch 'update/add-welcome-widget-banner-bg' into trunk
retrofox May 25, 2026
b5d6eb9
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 25, 2026
3a3ff5f
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 25, 2026
a897825
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 25, 2026
f2917e0
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 25, 2026
4625fb1
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 25, 2026
c683724
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 25, 2026
853b9c8
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 25, 2026
0780c53
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 25, 2026
75810ab
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 26, 2026
9d8276a
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 26, 2026
b19ab13
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 26, 2026
1a142a3
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 26, 2026
59b8492
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 26, 2026
393837e
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 27, 2026
9c43e72
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 27, 2026
461a10d
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 27, 2026
e0fb835
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 28, 2026
b0b47e5
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 28, 2026
9eed995
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 28, 2026
84e31b1
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 28, 2026
28819df
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 28, 2026
5d29de8
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 28, 2026
c8260b6
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 29, 2026
ffa81fb
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox May 29, 2026
51c1663
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 1, 2026
82bd130
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 1, 2026
cdf4af3
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 2, 2026
a5afaf0
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 3, 2026
43e6540
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 3, 2026
32606e6
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 3, 2026
64caef9
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 3, 2026
6087db1
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 3, 2026
66c67b0
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 4, 2026
a9e9dca
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 5, 2026
da70022
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 6, 2026
d36316e
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 6, 2026
d0cf2db
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 8, 2026
b5c5c0f
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 9, 2026
23472a4
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 10, 2026
b09e270
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 10, 2026
cfde402
extract widget-primitives into its own package
retrofox Jun 10, 2026
0c0c315
define WidgetIcon locally, drop @wordpress/ui dep
retrofox Jun 10, 2026
5d57660
declare sideEffects, document setup and changelog
retrofox Jun 10, 2026
7b18150
tighten public surface and README example
retrofox Jun 10, 2026
90f64e8
add dashboard widget system architecture doc
retrofox Jun 10, 2026
1f33bf7
add WidgetRender stories with a settings panel
retrofox Jun 10, 2026
967bc53
align stories with component status conventions
retrofox Jun 10, 2026
cff4ed3
add svg pipeline diagram to the widgets doc
retrofox Jun 10, 2026
d1f7a97
render the intro diagram from the local asset
retrofox Jun 10, 2026
f3ab3a3
drop the widget's own setter from WithSettings
retrofox Jun 10, 2026
030dfdf
keep host language neutral across docs and stories
retrofox Jun 10, 2026
f77a588
audit jsdoc and docs for host-neutral language
retrofox Jun 10, 2026
2b7824a
mark the message field required in WithSettings
retrofox Jun 10, 2026
b2e1549
registration is not loading: each host picks its strategy
retrofox Jun 10, 2026
b2f0185
load form control stylesheets in the stories
retrofox Jun 10, 2026
00420ca
add WithHostChrome story reading type metadata
retrofox Jun 10, 2026
9f94692
scope chrome wording to the story, not the system
retrofox Jun 10, 2026
5570890
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 12, 2026
0d23088
expand widget render story descriptions
retrofox Jun 12, 2026
3e1c3a6
stripe the chrome area in WithHostChrome
retrofox Jun 12, 2026
ab9172f
greet other worlds in the demo widget
retrofox Jun 12, 2026
3e448b6
tighten host and consumer vocabulary
retrofox Jun 12, 2026
dc2a528
add widget-primitives to the docs manifest
retrofox Jun 12, 2026
244729e
align package.json with core publish conventions
retrofox Jun 12, 2026
f17c3af
include presentation in the discovery steps
retrofox Jun 12, 2026
87228d1
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 14, 2026
27ccf97
Merge branch 'trunk' into add/widget-primitives-package
retrofox Jun 14, 2026
d13383b
update story to renamed background/foreground tokens
retrofox Jun 14, 2026
d4036b0
align README with core package doc style
retrofox Jun 15, 2026
24823c0
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 15, 2026
d0e9a46
Merge branch 'trunk' into add/widget-primitives-package
retrofox Jun 15, 2026
ae0b519
Merge branch 'trunk' into add/widget-primitives-package
retrofox Jun 15, 2026
21d2816
Merge branch 'trunk' into add/widget-primitives-package
retrofox Jun 15, 2026
2867de3
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 16, 2026
f4e14e4
Merge branch 'trunk' of github.com:WordPress/gutenberg into trunk
retrofox Jun 16, 2026
09850e8
move @types/react to peerDependencies
retrofox Jun 16, 2026
49f842a
Merge branch 'trunk' into add/widget-primitives-package
retrofox Jun 16, 2026
bce4af1
update lock file
retrofox Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
83 changes: 83 additions & 0 deletions docs/explanations/architecture/dashboard-widgets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# The Dashboard Widget System

<div class="callout callout-alert">
The widget system is experimental and ships behind the `gutenberg-dashboard-widgets` experiment. APIs and file conventions may change.
</div>

This document explains how the pieces of the dashboard widget system relate to each other: the authoring convention, the build pipeline, the server-side registry, the client contract published as `@wordpress/widget-primitives`, and the hosts that render widgets.

## Overview

A widget travels through five stations, each owned by a different part of the codebase:

![The widget pipeline: from the widgets folder, through the build and the server registry, to the client package and hosts](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/explanations/architecture/assets/dashboard-widgets-pipeline.svg)

No station knows about the internals of the next one; each consumes a narrow artifact (a folder convention, a manifest, a registry, a REST record, a `WidgetType`). That separation is what lets each piece evolve independently and is the reason the client contract lives in its own package.

## Authoring: a widget is a folder

A widget is a directory under `widgets/`, discovered by convention; there is no registration call to write:

```
widgets/hello-world/
├── widget.json static metadata (name, title, description, category, presentation)
├── widget.ts metadata module: default-exports title, icon, attributes, example
├── render.tsx render module: default-exports the React component
└── style.module.css optional, injected at runtime by the build
```

The split between `widget.json` and `widget.ts` is deliberate. `widget.json` is build-time input: plain JSON the pipeline can read without executing code. `widget.ts` is the live half of the metadata: values that only exist in JavaScript, such as the icon element or the `attributes` field schema that hosts feed into `DataForm`.

The render component receives the contract props and nothing else:

```tsx
export default function HelloWorld( { attributes, setAttributes } ) { ... }
```

## Build: from folders to script modules

`@wordpress/build` (`packages/wp-build/`) is the generic build tool for packages, routes, and widgets. For widgets specifically, it:

1. Discovers every directory under `widgets/` and reads its `widget.json`.
2. Compiles two ES script modules per widget: `render` (from `render.*`) and `widget` (from `widget.*`), each with an `*.asset.php` carrying module dependencies and a version hash. Missing source files simply produce no module; both are optional.
3. Emits `build/widgets/registry.php`, the manifest: one entry per widget with its directory name, metadata, and which modules were built.
4. Emits `widget-registration.php`, which at `init` calls `wp_register_script_module()` for every built module, with IDs derived from the folder name (`<prefix>/widgets/<dir>/render` and `<prefix>/widgets/<dir>/widget`).

The output of the build is therefore two things: registered script modules (loadable by the browser through the import map) and a manifest (readable by PHP without executing any JavaScript).

## The server registry

`WP_Widget_Type_Registry` (`lib/experimental/dashboard-widgets/`) is a singleton hydrated at `init` from the manifest: each entry becomes a `WP_Widget_Type` with `name`, `render_module`, `widget_module`, and `presentation`. The hydration is a deterministic copy of the manifest, with no filters in between: the `widgets/` folder is the single source of widget authorship in this codebase.

The registry is the server's runtime answer to "which widget types exist on this site", and two consumers read it:

- The REST controller (`WP_REST_Widget_Modules_Controller`) exposes it at `/wp/v2/widget-modules`, returning `{ name, render_module, widget_module, presentation }` per record.
- The dashboard page hooks the (otherwise generic) `{page-id}-wp-admin_boot_dependencies` filter to add every registered module to its import map as a `dynamic` dependency: reachable by `import()`, never eagerly executed.

Registration makes the modules known to WordPress; loading them is a separate, per-host decision. Dynamic `import()` against the import map is how the dashboard loads widgets today, but a host can equally enqueue a module eagerly (`wp_enqueue_script_module()`), declare it as a `static` dependency of its own module, or, outside WordPress, skip the import map entirely and resolve modules through its own `ResolveWidgetModule`.

The registry exists as a class (rather than the manifest being read directly by REST) so that the _source_ of widget types stays an implementation detail. Today the only source is the build manifest; a plugin-facing registration API would target the registry without touching the pipeline behind it.

## The client contract: `@wordpress/widget-primitives`

The package is the single source of truth for what a widget _is_ on the client, shared by widget authors and hosts. It exposes three kinds of resources and deliberately nothing else:

- **Contract types**: `WidgetType`, `WidgetName`, `WidgetIcon`, `WidgetRenderProps`, `ResolveWidgetModule`. Authors type `widget.ts` / `render.tsx` against them; hosts consume the same shapes. Nothing re-exports them.
- **Discovery**: `useWidgetTypes()` reads the `widgetModule` core-data entity (backed by `/wp/v2/widget-modules`), dynamically imports each record's `widget_module` to retrieve the live metadata, and merges both halves into `WidgetType[]`. The record's `presentation` (originating in `widget.json`) wins over the module's value.
- **Rendering**: `<WidgetRender>` resolves a `WidgetType.renderModule` through a host-provided `ResolveWidgetModule` and mounts the component with the `attributes` / `setAttributes` contract. On a WordPress page the resolver can be as simple as `( id ) => import( id )`, provided the hosting page exposed the module in its import map; hosts with other loading strategies supply their own resolver.

Equally important is what the package does not do: no chrome, no layout, no persistence, no data store of its own, and no knowledge of any host. That is what makes it publishable and consumable outside the WordPress admin.

## Hosts

A host is any context that renders widgets; the contract privileges none of them. The dashboard (`routes/dashboard/widget-dashboard/`) is the host this repository ships today, and it illustrates what a host owns: it calls `useWidgetTypes()`, owns the layout array and its persistence, wraps every instance in its own chrome (header, toolbars, error boundary, Suspense fallback), and passes `resolveWidgetModule` down through its context (overridable for tests and Storybook).

The same `WidgetType` could equally be rendered by a sidebar, a plugin panel, or an application outside wp-admin; the choice of where and how to render belongs entirely to the host. Every host is a consumer of the package; not every consumer is a host: tests, Storybook, or a picker that only lists widget types consume the same contract without rendering anything.

## Why a standalone package

The pipeline above has a natural seam: everything up to the REST endpoint is WordPress infrastructure, and everything after `WidgetType[]` is host territory. The contract in between is small, stable, and needed by both sides, which is exactly the shape of a package:

- Widget authors depend on it to type their metadata and render components.
- Hosts depend on it to discover and mount widgets without knowing how they were built or registered.
- Neither side needs the other's dependencies: the package keeps its own footprint minimal (`core-data`, `data`, `element`, `i18n`, plus type-only `dataviews`).
12 changes: 12 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2891,6 +2891,12 @@
"markdown_source": "../packages/warning/README.md",
"parent": "packages"
},
{
"title": "@wordpress/widget-primitives",
"slug": "packages-widget-primitives",
"markdown_source": "../packages/widget-primitives/README.md",
"parent": "packages"
},
{
"title": "@wordpress/widgets",
"slug": "packages-widgets",
Expand Down Expand Up @@ -3083,6 +3089,12 @@
"markdown_source": "../docs/explanations/architecture/styles.md",
"parent": "architecture"
},
{
"title": "The Dashboard Widget System",
"slug": "dashboard-widgets",
"markdown_source": "../docs/explanations/architecture/dashboard-widgets.md",
"parent": "architecture"
},
{
"title": "User Interface",
"slug": "user-interface",
Expand Down
5 changes: 4 additions & 1 deletion docs/toc.json
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,10 @@
{
"docs/explanations/architecture/full-site-editing-templates.md": []
},
{ "docs/explanations/architecture/styles.md": [] }
{ "docs/explanations/architecture/styles.md": [] },
{
"docs/explanations/architecture/dashboard-widgets.md": []
}
]
},
{
Expand Down
34 changes: 34 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions packages/widget-primitives/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!-- Learn how to maintain this file at https://github.com/WordPress/gutenberg/tree/HEAD/packages#maintaining-changelogs. -->

## Unreleased

### New Features

- Initial experimental release.
- `<WidgetRender>`: host entry point that resolves a widget's render
module and mounts it with the `attributes` / `setAttributes` render
contract.
- `useWidgetTypes()`: discovery hook returning the widget types
registered on the current site.
- Contract types: `WidgetType`, `WidgetName`, `WidgetIcon`,
`WidgetRenderProps`, `ResolveWidgetModule`.
69 changes: 69 additions & 0 deletions packages/widget-primitives/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Widget Primitives

<div class="callout callout-alert">
This package is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
</div>

The host-agnostic toolkit for dashboard widgets: the contract types that define
what a widget is, plus the runtime to discover the registered widget types and
resolve their render modules. It belongs to no host.

## Installation

Install the module:

```bash
npm install @wordpress/widget-primitives --save
```

_This package assumes that your code will run in an **ES2015+** environment.
If you're using an environment that has limited or no support for such
language features and APIs, you should include [the polyfill shipped in
`@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill)
in your code._

## Setup

This package ships no stylesheets and injects no styles; there is nothing
to enqueue or import.

The contract types and `<WidgetRender>` work in any React application. The
`useWidgetTypes()` hook reads the `widgetModule` entity through
`@wordpress/core-data`, so it expects to run against a WordPress site that
exposes the `/wp/v2/widget-modules` REST endpoint.

## Public API

- `<WidgetRender>`: canonical entry point for any host that mounts a widget.
Resolves the widget's render module via a host-provided `resolveWidgetModule`
and mounts the resulting component with the standard `attributes` plus
`setAttributes` render contract. Suspense, error handling, and chrome are
host concerns and live outside the package.
- `useWidgetTypes()` → `[ widgetTypes, isResolvingWidgetTypes ]`: the
`WidgetType[]` available on the current page, plus a flag that is true while
they are still resolving.
- Contract types: `WidgetType`, `WidgetName`, `WidgetIcon`,
`WidgetRenderProps`, `ResolveWidgetModule`. `WidgetIcon` is a rendered SVG
element (typically one from `@wordpress/icons`); hosts pass it to their
icon primitive as is.

## Architecture

For how the full pipeline fits together (authoring, build, server registry, and
hosts), see the
[dashboard widget system architecture document](https://github.com/WordPress/gutenberg/blob/HEAD/docs/explanations/architecture/dashboard-widgets.md).

## Contributing to this package

This is an individual package that's part of the Gutenberg project.
The project is organized as a monorepo. It's made up of multiple
self-contained software packages, each with a specific purpose. The
packages in this monorepo are published to [npm](https://www.npmjs.com/)
and used by [WordPress](https://make.wordpress.org/core/) as well as
other software projects.

To find out more about contributing to this package or Gutenberg as a
whole, please read the project's main
[contributor guide](https://github.com/WordPress/gutenberg/tree/HEAD/CONTRIBUTING.md).

<br /><br /><p align="center"><img src="https://s.w.org/style/images/codeispoetry.png?1" alt="Code is Poetry." /></p>
Loading
Loading