---
title: "API Reference"
description: "The complete public API of the GlassHome widget SDK. Every export across the main entry, /schemas, and /vite, with signatures."
canonical: https://glasshome.app/docs/widget-api-reference
section: "SDK"
updated: 2026-06-27
---
# API Reference

Every public export of `@glasshome/widget-sdk`, with signatures, by entry point. This is the exhaustive reference; the [Widget SDK guide](/docs/widget-sdk) walks the important APIs with worked examples. Anything not listed here is internal and may change without notice.

## Entry points

| Import | Use it for |
|---|---|
| `@glasshome/widget-sdk` | Everything you use at runtime: `defineWidget`, components, hooks, the data layer, utilities, theming. |
| `@glasshome/widget-sdk/schemas` | Zod schemas for validating manifests and publish payloads. Used by the CLI and Hub. |
| `@glasshome/widget-sdk/vite` | Build plugins and helpers for `vite.config.ts`. The scaffold wires these for you. |
| `@glasshome/widget-sdk/tailwind-sources` | A CSS file the build feeds to Tailwind. Referenced by the scaffold; not imported in widget code. |

Most types named below are exported too, so you can annotate your own code. Reactive values follow SolidJS conventions: `Accessor<T>` is `() => T`, and any argument typed `Accessor<X> | X` accepts a plain value or an accessor.

> **Always import the data layer from the SDK:** Home Assistant hooks (`useEntity`, `useService`, …) are re-exported from the SDK on purpose. Importing them from <code>@glasshome/sync-layer</code> directly fails the build and would bundle a second, disconnected store. See <a href="/docs/widget-capabilities">Capabilities</a>.

---

## `@glasshome/widget-sdk` (main entry)

### Core

```ts
function defineWidget<C = Record<string, unknown>>(
  definition: WidgetDefinition<C>
): WidgetDefinition<C>
```

Declares a widget. When `definition.configSchema` is present, `defineWidget` auto-populates `manifest.schema` (JSON Schema) and `manifest.defaultConfig` from it. See [the guide](/docs/widget-sdk#definewidget).

```ts
const SDK_VERSION: string  // the installed package version, e.g. "1.2.0"
```

The host compares `SDK_VERSION` against each widget's `manifest.sdkVersion` range.

### Components

`Widget` carries its slots as static members (`Widget.Icon`, `Widget.Title`, …); the same components are also exported standalone.

| Export | Props (key) | What it is |
|---|---|---|
| `Widget` | `variant`, `tone`, `color`, `colorTo`, `gradient`, `loading`, `emptyState`, `gestures`, `class` | The container: shell, gestures, theming. See [Widget component](/docs/widget-sdk#widget-component). |
| `WidgetContent` / `Widget.Content` | `children`, `class?` | Layout wrapper. |
| `WidgetIcon` / `Widget.Icon` | `icon`, `color?`, `dimmed?`, `entityCount?` | Entity icon tile with adaptive color. |
| `WidgetTitle` / `Widget.Title` | `children` | Primary label. |
| `WidgetStatus` / `Widget.Status` | `children` | State text. |
| `WidgetValue` / `Widget.Value` | `children` | Numeric/formatted value. |
| `WidgetSliderFill` / `Widget.SliderFill` | `value` (0–100), `color?`, `isDragging?` | Animated background fill for slider widgets. |
| `WidgetDialog` | see [WidgetDialog component](/docs/widget-sdk#widgetdialog-component) | Settings/detail dialog. Renders no chrome of its own; inject UI via props. |

### Widget hooks

```ts
function useWidgetContext(): ReactiveWidgetContext
```
Reactive widget context. Must be called inside the `<Widget>` tree.
```ts
interface ReactiveWidgetContext {
  isEditMode: () => boolean;
  updateConfig: (config: Record<string, unknown>) => void;
  dimensions: () => { width: number; height: number };  // CSS px, (0,0) before first layout
  registerDialogOpener?: (open: ((tab?: string) => void) | null) => void;
  callService?: ServiceCallFn;  // present when capability-routed by the host
}
```

```ts
function useWidgetEntityGroup<TData = unknown>(
  options: UseWidgetEntityGroupOptions<TData>
): UseWidgetEntityGroupResult<TData>
```
Aggregate one or many entities with presets + empty state. Options take reactive `entities: Accessor<EntityView[]>`, an `aggregationMode` (`"light" \| "sensor" \| "switch" \| "binary-sensor" \| "none"`) or a custom `calculateGroupData`, plus `emptyStateConfig`, `sensorGroupType`, `allEntitiesMode`, and `minEntities`. Returns `entities`, `groupData`, `aggregatedData`, `emptyState`, `hasEntities`, and `count` accessors. Full tables in [the guide](/docs/widget-sdk#usewidgetentitygroupoptions).

```ts
function useWidgetDialog(defaultTab = "controls"): WidgetDialogReturn
```
Open/close + active-tab state for `WidgetDialog`. Returns `showDialog`, `setShowDialog`, `openDialog`, `closeDialog`, `activeTab`, `setActiveTab`, and a `dialogProps` bag to spread onto `<WidgetDialog>`.

```ts
function useWidgetGestures(
  config: () => GestureConfig,
  orientation?: () => "horizontal" | "vertical" | "square"
): GestureHandlers
```
Tap, hold, and slide gestures for any pointer. `GestureConfig` = `{ tap?, hold?: { action, delay? }, slide?: { value, onChange, min?, max?, orientation?, activationDelay? } }`. Pass the returned `GestureHandlers` to ``; call `.dispose()` in `onCleanup`.

```ts
function useReducedMotion(): Accessor<boolean>      // prefers-reduced-motion; false if unavailable (SSR-safe)
function useIntersectionPause(el: Accessor<Element | undefined>): Accessor<boolean>  // true while off-screen
```

| Export | Kind | What it is |
|---|---|---|
| `WidgetCtx` | SolidJS context | The context object behind `useWidgetContext`. Advanced; prefer the hook. |

### Config schema helpers

```ts
const widgetFields: {
  title(): ZodType;                                              // text input "Title"
  entityIds(domain: string, opts?: { deviceClass?: string }): ZodType;     // multi-select entity picker
  singleEntity(domain: string, opts?: { deviceClass?: string }): ZodType;  // single-select entity picker
  areaId(): ZodType;                                            // area picker dropdown
}
```
Pre-built Zod fields with `.meta()` annotations the host's form renderer reads. For anything else use Zod directly (`z.boolean()` → switch, `z.enum()` → dropdown). See [widgetFields](/docs/widget-sdk#widgetfields).

### Home Assistant data hooks

Reactive accessors over the host's live store. Each `entityId` arg accepts a string or an accessor. Reads are checked against your declared [capabilities](/docs/widget-capabilities).

| Export | Signature | What it is |
|---|---|---|
| `useEntity` | `(id: Accessor<string> \| string) => Accessor<EntityView \| undefined>` | One entity by id. |
| `useEntities` | `(ids: Accessor<string[]>) => Accessor<EntityView[]>` | Many entities from an id-array accessor. |
| `useArea` | `(id: Accessor<string> \| string) => Accessor<AreaView \| undefined>` | An area and its entities. |
| `useEntityHistory` | `(id) => Accessor<EntityHistoryData \| undefined>` | Recent state history. |
| `useEntityStatistics` | `(id, options) => Resource<StatisticValue[]>` | Long-term statistics; resource exposes `.loading` / `.error`. |
| `useForecast` | `(id) => Accessor<WeatherForecastsData \| undefined>` | Weather forecast for a weather entity. |
| `useCamera` | `(id) => { stream: Accessor<CameraStreamData \| null>; refresh: () => void }` | Reactive camera stream. |
| `useStore` | `<T>(selector: (s) => T) => Accessor<T>` | Direct store selector. Escape hatch. |
| `byDomain` | `Accessor<Record<string, string[]>>` | All entity ids grouped by domain. |

### Connection & locale hooks

| Export | Signature | What it is |
|---|---|---|
| `useConnection` | `() => { status; isConnected }` (accessors) | Live connection status. |
| `useHassConfig` | `() => Accessor<HassConfig \| null>` | Home Assistant config. |
| `useUnitSystem` | `() => Accessor<HassUnitSystem \| null>` | Unit system. |
| `useTemperatureUnit` | `() => Accessor<string>` | e.g. `"°C"`. |
| `useLocale` | `() => Accessor<string>` | BCP 47 locale. |
| `useCurrency` | `() => Accessor<string>` | ISO 4217 currency. |

### Services

```ts
function useService(): {
  callService: ServiceCallFn;
  turnOn:  (entityId: string | string[], data?: Record<string, unknown>) => Promise<void>;
  turnOff: (entityId: string | string[], data?: Record<string, unknown>) => Promise<void>;
  toggle:  (entityId: string | string[], data?: Record<string, unknown>) => Promise<void>;
}

type ServiceCallFn = (
  domain: string,
  service: string,
  data?: Record<string, unknown>,
  target?: { entity_id?: string | string[] }
) => Promise<void>;
```
Inside a host-mounted widget these route through the host's capability check (a `control` grant is required); in preview/tests they fall back to direct sync-layer calls. `useTurnOn()`, `useTurnOff()`, and `useToggle()` each return just the one shortcut.

### Entity utilities

| Export | Signature | What it is |
|---|---|---|
| `isEntityActive` | `(entity: EntityView \| null \| undefined) => boolean` | `true` for active states. |
| `getEntityAttribute` | `<T>(entity, name: string, fallback?: T) => T \| undefined` | Read one attribute with a fallback. |
| `hassMediaUrl` | `(path: string \| null \| undefined) => string \| undefined` | Resolve an HA media path (album art, camera image) to a loadable URL. See [Media URLs](#media-urls). |
| `countActiveEntities` | `(entities: EntityView[]) => number` | Count active entities. |
| `calculateLightGroup` | `(entities: EntityView[], allEntitiesMode?= false) => LightGroupResult` | Aggregate on/off, brightness, color across a group. |
| `calculateSensorGroup` | `(entities: EntityView[], groupType?: SensorGroupType = "mean", ignoreNonNumeric?= true) => SensorGroupResult` | Aggregate numeric sensors. |

`LightGroupResult`: `isGroup`, `state`, `isOn`, `isUnavailable`, `brightness` (0–255), `brightnessPercent` (0–100), `color`, `onCount`, `totalCount`, `description`.
`SensorGroupResult`: `isGroup`, `state`, `numericValue` (`number \| null`), `isUnavailable`, `unit?`, `description`, `memberValues?`.
`SensorGroupType`: `"min" \| "max" \| "mean" \| "median" \| "sum" \| "last" \| "range" \| "product" \| "std_dev"`.

### Media URLs

Home Assistant exposes media references (a media player's album art `entity_picture`, camera proxy paths) as origin-relative paths like `/api/media_player_proxy/...?token=...`. A bare `<img src>` resolves those against the dashboard's own origin and fails to load. `hassMediaUrl` resolves them through the host's same-origin media proxy, so the image loads both **at home and over remote access** (no extra permissions, the path keeps Home Assistant's own signed token).

```tsx
import { hassMediaUrl, useEntity, getEntityAttribute } from "@glasshome/widget-sdk";

const entity = useEntity(entityId);

const albumArt = () => {
  const e = entity();
  if (!e) return undefined;
  return hassMediaUrl(getEntityAttribute<string>(e, "entity_picture"));
};

<Show when={albumArt()}>
  <img src={albumArt()} alt="" />
</Show>;
```

- Absolute `http(s)://` URLs pass through unchanged.
- Missing or empty input returns `undefined`, so `<Show when={albumArt()}>` handles "nothing playing" for free.
- You do **not** need an external service (such as a cover-art API) for media that Home Assistant already serves: read `entity_picture` and wrap it.
- Available since widget-sdk **1.3.0**.

### Theming & charts

| Export | Signature | What it is |
|---|---|---|
| `isDark` | `() => boolean` | Current theme as a boolean. |
| `injectTokens` | `(root?: Document \| ShadowRoot) => void` | Attach SDK CSS tokens to a root the host did not set up. |
| `svgColors` | `Record<SvgColorKey, { solid; stroke; fill }>` | Energy chart colors. Keys: `solar`, `grid`, `battery`, `ev`, `home`, `positive`, `negative`. See [Charts](/docs/widget-styling#charts). |
| `ToneSchema` | Zod enum | Validates a `Tone`. |
| `monotoneCubicPath` | `(points) => string` | Smooth SVG path string from a list of points. |

`Tone`: `"success" \| "warning" \| "danger" \| "info" \| "neutral" \| "accent"`.

### Validation re-exports

Convenience re-exports also available from [`/schemas`](#glasshomewidget-sdkschemas).

| Export | Kind | What it is |
|---|---|---|
| `WidgetManifestSchema` | Zod schema | Validate a complete widget manifest. |
| `formatSchemaError` | `(err) => string` | Turn a Zod error into a readable message. |

### Lower-level data exports

Advanced building blocks the hooks are built on. Most widgets never touch these.

| Export | Kind | What it is |
|---|---|---|
| `state` | `Store<GlassHomeState>` | The raw reactive store. |
| `getForecasts` | function | Imperative forecast fetch. |
| `trackEntityHistory` / `untrackEntityHistory` | `(entityId) => void` | Start / stop history tracking for an entity. |
| `getStream` | function | Camera stream descriptor. |
| `getWebRtcClientConfig` | function | WebRTC client config for camera streaming. |
| `startWebRtcSession` | function | Begin a WebRTC camera session. |
| `sendWebRtcCandidate` | function | Send an ICE candidate during a session. |

### Types (main entry)

```ts
interface WidgetDefinition<C = Record<string, unknown>> {
  manifest: WidgetManifest;
  configSchema?: ZodType<C, unknown>;
  migrate?: (config: Record<string, unknown>, fromConfigVersion: number) => Record<string, unknown>;
  component: (props: { config: C }) => any;
}

interface WidgetManifest {
  name: string;
  description?: string;
  minSize: GridSize;          // { w, h }
  maxSize: GridSize;
  defaultSize?: GridSize;
  sdkVersion: string;
  icon?: string;
  capabilities?: CapabilityGrant[];
  configVersion?: number;
  schema?: object;            // auto-populated from configSchema; do not set
  defaultConfig?: Record<string, unknown>;  // auto-populated; do not set
  cssUrl?: string;            // set by the build
}

interface GridSize { w: number; h: number }
```

Other exported types: `WidgetContext`, `ReactiveWidgetContext`, `EntityView` (see [the shape](/docs/widget-sdk#the-entityview-shape)), `AreaView`, `WidgetVariantConfig`, `WidgetStyles`, `Tone`, `SvgColorKey`, `GestureHandlers`, `AggregationPreset`, `SensorGroupType`, `LightGroupResult`, `SensorGroupResult`, `UseWidgetEntityGroupOptions`, `UseWidgetEntityGroupResult`, `WidgetDialogProps`, `WidgetDialogTab`, `WidgetDialogReturn`, `ServiceCallFn`.

---

## `@glasshome/widget-sdk/schemas`

Zod schemas re-exported from the canonical `@glasshome/widget-contract` package (the single source of truth shared by the SDK, the CLI, the Hub, and Dash), so the same definitions validate a manifest everywhere.

| Export | Kind | What it is |
|---|---|---|
| `widgetManifestSchema` (alias `WidgetManifestSchema`) | Zod schema | Validate a complete widget manifest. |
| `publishManifestSchema` | Zod schema | Validate the manifest subset sent at publish time. |
| `capabilitiesSchema` | Zod schema | Validate a `capabilities` array. |
| `capabilityGrantSchema` | Zod schema | Validate one `{ domain, access }` grant. |
| `GridSizeSchema` | Zod schema | Validate `{ w, h }`. |
| `parseGridSize` / `serializeGridSize` | functions | Convert grid size to/from the stored JSON form. |
| `PublishRequestSchema` / `PublishConfirmSchema` / `PublishBodySchema` | Zod schemas | Publish-flow request bodies. |
| `formatSchemaError` | `(err) => string` | Turn a Zod error into a readable message. |
| `CapabilityGrant` | type | One capability grant. |

---

## `@glasshome/widget-sdk/vite`

Build plugins and helpers for `vite.config.ts`. The scaffolded config wires these automatically; reach for them only for custom setups. See [Build tooling](/docs/widget-sdk#build-tooling).

| Export | Signature | What it is |
|---|---|---|
| `glasshomeWidget` | `(options?: GlasshomeWidgetOptions) => Plugin[]` | Single-widget plugin: dev preview + library build. |
| `glasshomeWidgets` | `(options?: GlasshomeWidgetsOptions) => Plugin[]` | Multi-widget plugin: per-widget builds + `registry.json`. |
| `buildWidgets` | `(options?: BuildWidgetsOptions) => Promise<void>` | Programmatic build of all widgets (used by `bun widget build`). |
| `discoverWidgets` | `(srcDir: string) => DiscoveredWidget[]` | Scan `src/` for subdirs with `index.tsx` + `manifest.json`. |
| `generateRegistry` | `(srcDir: string, outDir: string) => void` | Write a `registry.json` from discovered manifests. |
| `isWidgetExternal` | `(id: string) => boolean` | `true` for host-provided packages (kept out of the bundle). |

`BuildWidgetsOptions` / `GlasshomeWidgetsOptions`: `{ srcDir?, outDir?, plugins?, viteConfig?, only? }`. `GlasshomeWidgetOptions`: `{ entry?, … }`. All three types are exported.

---

## Dialog UI components (from `@glasshome/ui`)

`WidgetDialog` takes its chrome as props. These are **not** SDK exports; import them from `@glasshome/ui/solid` and pass them in (keep `@glasshome/ui` as a dependency). See [WidgetDialog component](/docs/widget-sdk#widgetdialog-component).

`ResponsiveDialog`, `ResponsiveDialogContent`, `ResponsiveDialogHeader`, `ResponsiveDialogTitle`, `ResponsiveDialogDescription`, `Button`, `SchemaForm` (`SchemaForm` is required only when you use `configSchema`).