Repository navigation
Dashboard Overview #77616
Description
This is an overview issue for a customizable WordPress dashboard, a widget-based surface users can arrange, resize, and compose.
Last updated: 2026-07-29.
A lot has landed since this was opened, so the statuses below have been brought up to date. Package names have shifted as pieces got extracted, and those are noted inline.
It builds on the design direction in #74616 and sits downstream of the broader admin work in #70913, where Widget is defined as a first-class primitive.
The direction is reflected in the WordPress 7.0 planning post ("bring grid layout customization to dashboard experience or other screens") and in the 7.0 product review, which captured the desire to turn the dashboard into a more customizable homepage with remixable widgets.
All runtime code lands behind a new gutenberg-dashboard-widgets experiment while the APIs settle. Nothing is promoted out of __experimental until the shape is proven in real use.
Architecture
The dashboard is composed of three layers, each depending only on the layer below.
Identity (what a widget is) is decoupled from rendering (where and how a widget is mounted),
which keeps widget types reusable across surfaces: dashboards, sidebars, plugin panels, future screens.
The build pipeline discovers widgets and emits a registry.
On the server, widget-types.php hydrates WP_Widget_Type_Registry from that manifest at init and exposes it over REST at wp/v2/widget-modules; on the client, @wordpress/widget-primitives reads that endpoint through a @wordpress/core-data entity and resolves each widget's render module on demand.
There is no custom store, no injected global, and no per-page wiring: the registry is global, and hosts decide when to import a widget, not whether it is available.
%%{init: {
"flowchart": {
"padding": 26,
"nodeSpacing": 78,
"rankSpacing": 90,
"subGraphTitleMargin": { "top": 21, "bottom": 23 },
"curve": "basis"
},
"themeVariables": {
"edgeLabelBackground": "transparent",
"fontSize": "20px"
}
}}%%
graph TB
subgraph s1["<span style='font-size:22px;font-weight:600'>① Build · @wordpress/build</span>"]
direction LR
SRC["widgets/{name}/<br/>widget.json · widget.ts · render.tsx"]
OUT["build/widgets/registry.php<br/>build/widgets.php"]
SRC --> OUT
end
subgraph s2["<span style='font-size:22px;font-weight:600'>② Registry · PHP + REST</span>"]
direction LR
REG["widget-types.php →<br/>WP_Widget_Type_Registry<br/>hydrated from manifest at init"]
REST["REST wp/v2/widget-modules<br/>name · render_module · widget_module"]
REG --> REST
end
subgraph s3["<span style='font-size:22px;font-weight:600'>③ Toolkit · @wordpress/widget-primitives</span>"]
direction LR
ENTITY[("core-data<br/>widgetModule entity")]
API["useWidgetTypes() · <WidgetRender><br/>lazy import render_module"]
ENTITY --> API
end
HOSTS["Hosts · dashboard, sidebars, plugin panels, …"]
s1 ==> s2
s2 ==> s3
s3 ==> HOSTS
classDef default fill:#8898a815,stroke:#8898a8,stroke-width:1px,rx:13,ry:13
classDef accent fill:#f59e0b15,stroke:#f59e0b,stroke-width:1.5px,rx:13,ry:13
classDef layer fill:transparent,stroke:#8898a880,stroke-width:1px,rx:18,ry:18
class HOSTS accent
class s1,s2,s3 layer
Grid primitive ✅ Done
A reusable grid primitive for tile layouts, drag to reorder, resize, fill, and full-width spans. Designed to be usable outside the dashboard as well.
@wordpress/grid shipped in #77562 and is now at v0.3.0.
The follow-up we'd left open (exploring more grid models) landed too: DashboardLanes, a masonry surface built on the display: grid-lanes spec, now sits next to the original DashboardGrid (#78107). Both share the same layout-as-data contract, with keyboard-accessible drag to reorder and resize handles.
Open follow-ups on the primitive: per-item size limits (#81899, the grid side of #81795), tile memoization so gesture frames re-render only the active tile (#81959), and systematic performance measurement across tile counts (#81960).
Build pipeline for widgets ✅ Done
A convention for authoring widgets (metadata + render component) that is auto-discovered and registered with minimal boilerplate, following the same file-based pattern used elsewhere in the build system.
widgets/ folder support shipped in @wordpress/build (#77347). One piece that wasn't in the original scope: we also added @wordpress/dashboard-init to register widget-module discovery at boot, before the page renders (#79394).
Widget registration API 🟡 In progress (approach changed)
A surface-agnostic registry for widget types, so any screen can consume them. Includes a PHP-side registration API that bridges into a client-side store, mirroring how block types are wired in core. No coupling to any particular surface.
Heads up on direction: the contract landed as a typed runtime registry, built up piece by piece, rather than being defined by a schema up front.
The widget.json JSON Schema is still being published for editor tooling (#77640), now mirroring the settled shape: the nine JSON-expressible fields the build actually reads. Runtime values (icon, attribute fields, example data) stay in the widget's JS module.
widget-types was renamed to @wordpress/widget-primitives and split into its own package.
What's landed so far: REST endpoint plus core-data entity (#77987), a declarative help field (#79830), declarative actions (#80363), server-side translation for title, description and keywords (#79701), and a typed field registry (#80148). The remaining piece is the JSON Schema publication (#77640), which closes out #77629.
Rendering engine 🟡 In progress (very active)
The dashboard rendering surface is built on the grid primitive and the widget registry. Defines the widget shell (header, content, actions) and composes the full dashboard, including inserter and empty-state affordances. Layout flows in as props; persistence is a separate concern.
It's its own package now, @wordpress/widget-dashboard (used to be part of @wordpress/dashboard, renamed in #79268).
The experimental engine landed early (#77770). Widget shells, per-instance chrome, and inserter affordances are landing incrementally.
The host seam landed in #81740: WidgetHostProvider / useWidgetHost in @wordpress/widget-primitives, with a first links capability, so action links to the app's own routes navigate client-side. The dashboard's second route, Site Health, is their first in-app target (#81729).
Layout persistence ✅ Done
User-saved dashboard layouts, with plugin-provided defaults that users can override, and a reset-to-default action.
This never got its own tracking issue, but it already shipped:
- Persistence through
@wordpress/preferences(Dashboard: persist layout via @wordpress/preferences #78034). - A backend default layout filter (Dashboard: backend default layout filter #78040).
- A REST endpoint for the default layout (Dashboard: REST endpoint for the default layout #78066).
- A staging layer so in-progress edits don't commit until you confirm them (Dashboard: staging layer for in-progress layout edits #78071).
First-party widgets
A set of widgets adapted to the new shell. Scope grew past the original three.
Shipped:
- Quick Draft, modernized onto the new shell (Dashboard Widgets: Adapt Quick Draft to its tile size with a recent drafts list #78572).
- Activity (Add dashboard Activity widget #78552).
- Site Health (Add dashboard Site Health widget #78555).
- Site Preview (Add dashboard Site Preview widget #78556), with URL bar (Dashboard: URL bar in site preview widget #78656).
- News (Add dashboard News widget #78554).
- Events (Dashboard: add Events widget #78553), with a follow-up iteration (Dashboard: event widget iteration #78815).
- Welcome (Add welcome dashboard widget with adaptive layout and content #78461).
- Quick Post (Add
Quick draftwidget #78408). - Hello Dolly (Dashboard: Hello Dolly #78648).
Still open:
- New dashboard widget: On this day... #74936. Still a design proposal, no PR yet.
Important
This is meant to be an ongoing working document. Tracking issues and PRs will be linked back as they're opened, and the structure will evolve as pieces land.
Activity
widget.jsonmetadata schema #77629width: 'fill'ignores tiles spanning multiple rows #77768annezazu commentedon May 28, 2026
Hi @retrofox can you create an iteration issue for 7.1 for this? Here's more information.
retrofox commentedon Jun 12, 2026
I totally missed this ping, @annezazu :'(. I'm going to do it asap in case it's still relevant.
annezazu commentedon Jun 13, 2026
It's relevant if you intend to land work for 7.1. If not, no worries! If you do, please do create one.