Skip to content

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

ToolVersionNotes
Bun1.0+CLI engines field requires bun >=1.0.0
Node.js18+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. 1

    Scaffold a project

    bunx @glasshome/widget-cli@latest
    cd <project-name>

    Generates package.json, vite.config.ts, tsconfig.json, the widget script alias, and your first widget (prompts for name + description).

  2. 2

    (Optional) add more widgets

    create already scaffolds your first widget. To add another:

    bun widget add

    Prompts for name and description, creates src/<name>/index.tsx and src/<name>/manifest.json.

  3. 3

    Connect to a running Dash instance

    bun widget connect http://homeassistant.local:3123

    Builds, runs an OAuth device-code flow against your local Dash instance (not Hub), uploads each bundle to the Dash API under the local scope, enables dev mode, then watches src/ and re-uploads on save. Widgets refresh without reloading the page.

  4. 4

    Publish

    bun widget publish

    publish runs login automatically 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. useWidgetEntityGroup then 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 typeWhere it livesWho bumps itWhat breaks without it
SDK version (sdkVersion)manifest.json, as a semver range (e.g. ^1.0.0)bun widget upgrade after you update the depDash may refuse to load bundles built against a wildly different SDK
Widget version (version)manifest.json--bump flag on bun widget publishRe-publishing the same version is rejected (409)
Config version (configVersion)manifest.json, integerYou, when config shape has breaking changesWithout a bump, users’ saved configs aren’t migrated and may fail Zod parse
CLI minimum (Hub-enforced)Served by Hub at /api/widgets/cli-versionHub (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 connect runs 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 login runs an OAuth PKCE flow against GlassHome Hub (glasshome.app). A browser opens to Hub; the callback lands on http://127.0.0.1:9274/callback (or localhost). The resulting token is used for publish.

Where to go next