---
title: "Getting Started"
description: "Build custom GlassHome widgets with SolidJS and the widget SDK. Scaffold, develop with hot-reload, validate, and publish."
canonical: https://glasshome.app/docs/widgets/widget-development
section: "Build Widgets"
updated: 2026-08-16
---
# 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.

Inside a scaffolded project, `bun widget <cmd>` works (the CLI is pinned in `devDependencies`, with a `widget` script alias to `glasshome-widget`); elsewhere use `bunx @glasshome/widget-cli@latest <cmd>`. Full command list: [Widget CLI](/docs/widgets/widget-cli).

## 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](https://mise.jdx.dev) and run `mise install`.

## Quickstart

  1. ```bash
       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. ```bash
       bun widget add
       ```
       Prompts for name and description, creates `src/<name>/index.tsx` and `src/<name>/manifest.json`.

  3. ```bash
       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/`, re-uploading on save; widgets refresh without a page reload.

  4. ```bash
       bun widget publish
       ```
       `publish` runs `login` automatically the first time. See [Publishing](/docs/widgets/widget-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 handles rendering, gestures, theming, entity subscriptions, settings forms, and layout. Your widget supplies how the bound entities are visualized and what the primary tap does.
- No settings UI to write: define a Zod schema; the dashboard renders the form.
- No WebSocket state to manage: `useEntity` / `useEntities` read the entity ids the user picked in config and stay reactive; `useWidgetEntityGroup` aggregates them and supplies an empty state.
- No Home Assistant connection or token to hold: all reads and service calls go through SDK hooks, checked against your declared [capabilities](/docs/widgets/widget-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` (bumps the dep and rewrites the range) | 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

Both flows open a browser, but they authenticate against different hosts, and the resulting tokens are stored separately, never interchangeable:

## Where to go next

  ### [Widget SDK guide](/docs/widgets/widget-sdk)

defineWidget, Widget components, hooks, the HA data layer, entity helpers.
  ### [API Reference](/docs/widgets/widget-api-reference)

Every public export across the SDK, /schemas, and /vite entry points.
  ### [Capabilities & Permissions](/docs/widgets/widget-capabilities)

Declare the Home Assistant access your widget needs and how the user approves it.
  ### [Styling & Animation](/docs/widgets/widget-styling)

Per-widget CSS bundles, Tailwind, your own CSS files, theme variables, container queries, animation.
  ### [CLI reference](/docs/widgets/widget-cli)

Every `bun widget <command>` and what it does.
  ### [Publishing](/docs/widgets/widget-publishing)

Auth, scopes, versioning, trust badges.
  ### [Config migrations](/docs/widgets/widget-migrations)

Handle breaking config changes without breaking users.