Dashboard widgets (dashboard:widgets)
The Dashboard widget (Dashboard widget)area is an editable widget grid (much like a phone home screen): the built-in watermark, account, announcement, and instance launcher are all widgets in the grid, and plugins can add their own UI as widgets too. Users drag, resize, add, and remove plugin widgets just like built-in ones.This page covers plugin widgets (the
dashboard:widgets slot). To write XML widgets directly, see Dashboard custom widgets. The two mechanisms are independent.Quick start
Declare adashboard:widgets Slot (slot) in contributes.slots of manifest.json:
widget.html is a standalone HTML page (same Sandbox environment as entry.frontend, with the full __PLUGIN_API__ Bridge). After the plugin is installed and enabled:
- When the plugin activates, the Launcher mounts
widget.htmlas an iframe Sandbox and registers it in the dashboard widget registry; - The grid automatically appends the widget to the end (default 2 × 1 cells); you do not need to register anything manually;
- Users can move and resize it in edit mode, exactly like any other widget.
dashboard:widgets slots (multiple widget.html files), and each becomes its own widget.
Grid model
The grid is 4 columns × N rows, and every widget occupies whole cells:- Free placement: drag a widget to any free spot; it does not snap upward automatically (similar to a Windows desktop with “auto arrange icons” off), and its old spot stays empty after you move it.
- Resize: drag the handle on the bottom-right / right / bottom edge; it snaps to cells and can shrink to 1 × 1.
- Dynamic row height: row height scales with the window (roughly 64–84px), so widget physical size follows the viewport.
- Persistent layout: the layout users adjust is stored locally by the Launcher (localStorage) and survives Launcher upgrades.
contributes.slots[] supports width / height to fix the iframe pixel size (for fixed slots such as header:right). Do not set them on dashboard:widgets: a fixed size stops the iframe from stretching to its cells, so content overflows or leaves blank space after a resize.
Responsive sizing
Users can resize a widget to anything between 1×1 (about 250 × 70px) and 4×4 (about 1050 × 560px). A good approach is “show brief information when small, detailed information when large”:ResizeObserver callback fires after the user releases the resize handle (it does not update frame by frame while resizing), so you do not need your own throttling.
How users add and edit widgets
Users click Edit layout (Edit layout) in the top-right corner of the dashboard to enter edit mode:
When the toolbar is not shown (not in edit mode), widgets return to static display and buttons and links inside the grid work normally.
Lifecycle
Constraints and notes
- Sandbox environment: widget.html runs in an
allow-scriptsiframe Sandbox with the same capabilities as theentry.frontendpage (postMessage Bridge,.p-*styles,registerMethod, and so on). See the plugin API reference for details. - No Launcher context: a widget cannot see its own position or size metadata in the grid (for example “how many cells do I span”); it can only read physical pixel size through
ResizeObserver. - Widget identity: widget ids are generated by the Launcher as
plugin:{plugin id}:{index}and cannot be customized. When one plugin declares several slots, they are numbered in Manifest declaration order. - Light and dark themes: the iframe gets the theme CSS variables automatically, so use
var(--foreground),var(--card), and similar tokens to follow the theme; never hardcode colors. - Overflow clipping: the outer card uses
overflow: hiddenwith rounded corners, so content beyond the cells is clipped. Make sure your layout adapts.