---
title: "Upgrading to SDK 1.0"
description: "What changed in widget-sdk 1.0 — shadow-root rendering, separate CSS, capability declarations, and no direct Home Assistant access — and how to migrate a widget."
canonical: https://glasshome.app/docs/widget-sdk-1-0
section: "Widget Development"
updated: 2026-06-12
---
# Upgrading to SDK 1.0

`@glasshome/widget-sdk` 1.0 is the security release. Widgets now run inside an isolated, permission-scoped sandbox. Most widgets that only use SDK hooks need small, mechanical changes. This page is the migration checklist.

> **Info:** Dash only loads widgets built against SDK 1.x. A widget published against an older SDK shows a **"needs update"** tile until you republish. Bump your `@glasshome/widget-sdk` dependency to `1.x`, work through this page, then `bun widget publish`.

## What changed

  ### Shadow-root rendering

Each widget renders in its own closed shadow root for style and DOM isolation. Your CSS no longer leaks into Dash or other widgets, and vice versa.
  ### Separate stylesheet

The build emits index.css next to index.js. Dash adopts it into your widget
  ### Capability declarations

Your manifest declares which Home Assistant domains the widget reads or controls. The user approves them at install; the host enforces them.
  ### No direct HA access

Widgets no longer import @glasshome/sync-layer or call fetch/WebSocket directly. All Home Assistant interaction goes through SDK hooks, which are permission-checked.

## Migration checklist

### 1. Bump the SDK dependency

```jsonc
// package.json
"peerDependencies": {
  "@glasshome/widget-sdk": "1.0.0"
}
```

And set the manifest range so Dash knows the widget targets 1.x:

```jsonc
// manifest.json
"sdkVersion": "^1.0.0"
```

### 2. Import data and service hooks from the SDK, not sync-layer

Direct `@glasshome/sync-layer` imports now **fail the build** with a clear error. The SDK re-exports every hook you used:

```diff
- import { useEntity, useService } from "@glasshome/sync-layer/solid";
+ import { useEntity, useService } from "@glasshome/widget-sdk";
```

This is what makes permissions enforceable: service calls route through the host's permission check instead of straight to Home Assistant.

### 3. Declare capabilities

Add a `capabilities` array to your manifest listing the Home Assistant domains your widget touches, `control` for ones you operate and `read` for ones you only display. A widget that never touches Home Assistant declares `[]`. Publishing a 1.x widget without the array is rejected.

```jsonc
// manifest.json
"capabilities": [
  { "domain": "light", "access": "control" },
  { "domain": "sensor", "access": "read" }
]
```

The full grammar (access levels, narrowing to specific entities or services, what the user approves, and how the host enforces it) is covered in **[Capabilities & Permissions](/docs/widget-capabilities)**. Declare the minimum: a widening update has to be re-approved by the user.

### 4. Don't fight the shadow root

Your widget now renders in an isolated shadow root with its own CSS bundle. Theme variables still inherit, so don't redefine them; keep `@glasshome/ui` as a dependency; and reach Home Assistant through SDK hooks rather than `window`/`document` globals. The full styling model is in **[Styling & Animation](/docs/widget-styling)**.

### 5. Rebuild and publish

```bash
bun run build          # emits index.js + index.css + an updated registry
bun widget publish --scope <your-scope>
```

The publish flow verifies the bundle hash server-side and validates your capability declaration before the version goes live.

## Testing locally first

`bun widget connect <dash-url>` builds and serves your widget to a running Dash, including the capability flow: install it, and you'll see the same permission prompt your users will. Confirm the widget renders correctly inside its shadow root (styling, theme, dialogs) and that its service calls work before publishing.