Guide
Integrate Gridmason into a React app
Start from an empty Vite project and finish with a live, drag-to-arrange widget dashboard — the engine embedded, a widget rendering, and edit mode wired to React state.
Gridmason's engine (@gridmason/core) is framework-agnostic: the canvas is a custom element, and widgets are custom elements too. "Using it in React" therefore means one thing — your React app is the host that mounts the canvas and hands it a resolved layout and an SDK handle. This guide walks the whole path, and every file below is copied verbatim from a project that was built and run.
What you'll build
A single page rendering two instances of a small acme-greeting widget on the grid canvas, with an Edit layout button that turns on drag & resize and streams geometry changes back into your app:
PrerequisitesBefore you start
- Node.js ≥ 22 and npm (the packages are ESM-only and target Node 22+).
- Basic familiarity with React and TypeScript.
- That's it — no registry, account, or Gridmason CLI is needed for this guide. The widgets are local and the host SDK is a dev/no-op handle.
Step 1Create a Vite React project
Scaffold a fresh React + TypeScript app and install its base dependencies:
$ npm create vite@latest gm-dashboard -- --template react-ts
$ cd gm-dashboard
$ npm install
Step 2Install Gridmason
Add the engine, the SDK, and gridstack (the canvas's drag/resize dependency, whose stylesheet you'll import):
$ npm install @gridmason/core @gridmason/sdk gridstack
Note. You don't install @gridmason/protocol directly — core and sdk pull the contract types in transitively, and this app imports every type it needs from @gridmason/core/engine and @gridmason/sdk. gridstack is a dependency of core, but installing it explicitly makes its CSS import (below) robust regardless of your package manager's hoisting.
Step 3Add the files
You'll create three new files and edit three of the generated ones:
├── widgets/
│ └── greeting-widget.ts # the widget: a custom element
├── gridmason/
│ ├── dashboard.ts # engine wiring: catalog, page type, layout
│ └── CanvasHost.tsx # the React ↔ canvas seam
├── App.tsx # edited — composes it all
├── App.css # edited — app + widget styles
├── index.css # edited — trimmed to a reset
└── main.tsx # edited — one import added
The widget
A Gridmason widget is a framework-agnostic custom element. Before connectedCallback runs, the canvas sets four attributes (settings, context, instance-id, edit-mode) and one property, sdk — the opaque host handle. This one reads its settings for a name, shows the host-issued instance id, and ticks a clock, cleaning up its timer on teardown.
Wire the engine
The headless engine is pure data — no DOM. Register the widget type in a WidgetCatalog, declare a page type, and resolve a starting LayoutDoc into the EffectiveLayout the canvas renders. Here it places two instances of the one widget.
The React ↔ canvas seam
Core ships no React wrapper, and it doesn't need one. <gm-page-canvas> is a custom element whose structured inputs (layout, context, sdk, editMode) are set as properties, so you mount it imperatively through a ref: create it once, assign its inputs, and push editMode when React state changes. It also forwards the canvas's gm:geometry-change event — fired after a user drag/resize settles — back to your app.
Compose the app
App.tsx registers the widget's custom element, builds the engine pieces once, mints a dev/no-op SDK handle with createNoopSDK(), and toggles edit mode. In production you'd swap the no-op for a real HostSDK implementation.
Styles and entry point
Give the canvas host a width, style the widget's light-DOM markup, trim the template CSS to a reset, and import gridstack's stylesheet in main.tsx (without it the grid items won't position).
Step 4Run it
$ npm run dev # http://localhost:5173
Open the dev server. You should see:
- Two “Hello, Gridmason!” and “Hello, React!” cards side by side, each with a ticking clock and its instance id — proof the widget received its
settingsand thesdkhandle. - A caption reading “1 widget type · 1 page type” — the catalog and page-type registrations ran.
- Clicking Edit layout makes the cards draggable and resizable. Move one and your browser console logs
geometry changedwith every item's new{ x, y, w, h, i }— thegm:geometry-changeevent flowing into React, ready to persist.
To make edits stick, feed that geometry back into a new LayoutDoc and re-resolve it — the canvas re-renders when the layout object identity changes. Persisting it (to localStorage or your backend) and reloading it through loadLayout is the natural next increment.
How it fits together
Three packages, three responsibilities:
@gridmason/core/engine— the headless, DOM-free half: the widget catalog, the page-type registry, andresolveLayout, which composes default/org/user layers into anEffectiveLayout.@gridmason/core/canvas—<gm-page-canvas>, the only DOM consumer. It mounts one already-registered custom element per placed item, inside a per-widget error boundary, and surfaces user edits as events. It loads nothing — your host ownscustomElements.define.@gridmason/sdk— theHostSDKinterface widgets code against for data, events, settings, and telemetry, plus the dev/no-op handle you used here. When you write a real host, you implement this interface and prove it with the SDK's conformance kit.
Next steps
Write a real widget
Scaffold, dev, lint, and publish a widget with the gridmason CLI — the same checks a registry review runs.
Implement a real HostSDK
Replace createNoopSDK() with a capability-enforcing handle backed by your data and permissions.
Run the conformance kit
Prove your host is “a valid Gridmason host” with the machine-checkable @gridmason/sdk/conformance suite.
Read the canvas ABI
The full widget lifecycle contract — attributes, the SDK handle, skeletons, the error boundary, and edit mode.
Reference docs: the core quickstart & engine API, the sdk README, and the dashboard's reference widgets.