Getting Started
A widget is a SolidJS component + a Zod config schema, wrapped with defineWidget. The dashboard renders the component, auto-generates the settings form from the schema, and feeds in entity state.
Scaffold
`bunx @glasshome/widget-cli@latest` (no args). Creates a project and your first widget.
Hot-reload
`bun widget connect <dashboard-url>` rebuilds on save and re-uploads to a running dashboard.
Publish
`bun widget publish` to Hub. Pick personal or organization scope.
The bun widget <cmd> form works inside scaffolded projects (they pin the CLI in devDependencies and ship a widget script alias to glasshome-widget). Outside a project, use bunx @glasshome/widget-cli@latest <cmd>. See Widget CLI for the full command list and details.
Prerequisites
| Tool | Version | Notes |
|---|---|---|
| Bun | 1.0+ | CLI engines field requires bun >=1.0.0 |
| Node.js | 18+ | Required by @glasshome/widget-sdk (engines.node >= 18); Bun’s bundled Node compatibility layer satisfies this |
Scaffolded projects ship a .mise.toml pinning the versions. Install mise and run mise install.
Quickstart
- 1
Scaffold a project
bunx @glasshome/widget-cli@latest cd <project-name>Generates
package.json,vite.config.ts,tsconfig.json, thewidgetscript alias, and your first widget (prompts for name + description). - 2
(Optional) add more widgets
createalready scaffolds your first widget. To add another:bun widget addPrompts for name and description, creates
src/<name>/index.tsxandsrc/<name>/manifest.json. - 3
Connect to a running Dash instance
bun widget connect http://homeassistant.local:3123Builds, runs an OAuth device-code flow against your local Dash instance (not Hub), uploads each bundle to the Dash API under the
localscope, enables dev mode, then watchessrc/and re-uploads on save. Widgets refresh without reloading the page. - 4
Publish
bun widget publishpublishrunsloginautomatically the first time. See Publishing.
Project layout
my-widgets/
├── package.json
├── vite.config.ts
├── tsconfig.json
└── src/
├── my-widget/
│ ├── index.tsx # Component + defineWidget
│ └── manifest.json # Widget metadata
└── another-widget/
├── index.tsx
└── manifest.json
One widget per directory under src/. Build outputs one self-contained JS bundle per widget plus a registry.json in dist/.
Mental model
- The dashboard already handles: rendering, gestures, theming, entity subscriptions, settings forms, layout.
- Your widget supplies: how the bound entities are visualized, and what the primary tap action does.
- You do not write settings UI. Define a Zod schema; the dashboard renders the form.
- You do not manage WebSocket state. Read entities with
useEntity/useEntities(from the ids the user picked in config) and the SDK keeps them reactive.useWidgetEntityGroupthen aggregates them and supplies an empty state. - You do not hold a Home Assistant connection or token. All reads and service calls go through SDK hooks and are checked against your declared capabilities.
Versions at a glance
| Version type | Where it lives | Who bumps it | What breaks without it |
|---|---|---|---|
SDK version (sdkVersion) | manifest.json, as a semver range (e.g. ^1.0.0) | bun widget upgrade after you update the dep | Dash may refuse to load bundles built against a wildly different SDK |
Widget version (version) | manifest.json | --bump flag on bun widget publish | Re-publishing the same version is rejected (409) |
Config version (configVersion) | manifest.json, integer | You, when config shape has breaking changes | Without a bump, users’ saved configs aren’t migrated and may fail Zod parse |
| CLI minimum (Hub-enforced) | Served by Hub at /api/widgets/cli-version | Hub (I bump it on protocol changes) | login/publish hard-stop if CLI is below the floor |
Auth flows: connect vs. login
Two separate auth flows exist for two separate systems:
bun widget connectruns an OAuth device-code flow against your local Dash instance. A browser opens to the Dash approval page. The token it issues is scoped to that Dash host and stored separately from Hub credentials.bun widget loginruns an OAuth PKCE flow against GlassHome Hub (glasshome.app). A browser opens to Hub; the callback lands onhttp://127.0.0.1:9274/callback(orlocalhost). The resulting token is used forpublish.
Where to go next
Widget SDK guide
defineWidget, Widget components, hooks, the HA data layer, entity helpers.
API Reference
Every public export across the SDK, /schemas, and /vite entry points.
Capabilities & Permissions
Declare the Home Assistant access your widget needs and how the user approves it.
Styling & Animation
Per-widget CSS bundles, Tailwind, your own CSS files, theme variables, container queries, animation.
CLI reference
Every `bun widget <command>` and what it does.
Publishing
Auth, scopes, versioning, trust badges.
Config migrations
Handle breaking config changes without breaking users.