Skip to content
Open
40 changes: 40 additions & 0 deletions blog/2026-04-29-registering-custom-dashboard-strategies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
author: Aidan Timson
authorURL: https://github.com/timmo001
title: Registering custom dashboard strategies
---

As of Home Assistant 2026.4, you can now register custom dashboard strategies, just as you can with [custom cards](/docs/frontend/custom-ui/custom-card), making them easier to discover and add using the **new dashboard** dialog under the **Community dashboards** section.

Before you could do this, but had to send users to create a blank dashboard, edit in YAML mode, and paste in your custom strategy. Now you can register a friendly name, description, documentation, and preview image.

To register your strategy, call `window.customStrategies.push()` with an object containing the following keys:

- `type`: The strategy type without the `custom:` prefix, for example `"my-demo"`.
- `strategyType`: The type of strategy, e.g. `"dashboard"`.
- `name` (`optional`): The friendly name of the strategy.
- `description` (`optional`): A short description of the strategy.
- `documentationURL` (`optional`): A URL to the documentation for the strategy. This is not shown in the strategy UI yet but may in the future.
- `images` (`optional`): A preview image, either a single URL or a light/dark pair.

Example:

```js
window.customStrategies = window.customStrategies || [];

window.customStrategies.push({
type: "my-demo",
strategyType: "dashboard",
name: "My demo dashboard",
description: "A starter dashboard generated from JavaScript.",
documentationURL: "https://example.com/my-demo-dashboard",
images: {
light: "/local/my-demo/preview-light.svg",
dark: "/local/my-demo/preview-dark.svg",
},
});
```

This metadata is separate from the custom element itself. Your strategy still needs to be registered with a tag like `ll-strategy-dashboard-my-demo`, and users still need the resource loaded before Home Assistant can discover it. You can use HACS for this as other resource can be added, like custom cards.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

Take a look at the updated [custom strategies](/docs/frontend/custom-ui/custom-strategy) documentation with example code and further details.
217 changes: 140 additions & 77 deletions docs/frontend/custom-ui/custom-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,122 @@ title: "Custom strategies"

_Introduced in Home Assistant 2021.5._

Strategies are JavaScript functions that generate dashboard configurations. When a user has not created a dashboard configuration yet, an auto-generated dashboard is shown. That configuration is generated using a built-in strategy.
Strategies are elements that generate custom dashboards and/or views. You can create new strategies in the frontend repository, or create custom strategies. Both of which have access to the Home Assistant API similar to [custom cards](./custom-card.md).
Comment thread
timmo001 marked this conversation as resolved.
Outdated

It's possible for developers to create their own strategies to generate dashboards. Strategies can use all of Home Assistant's data and the user's dashboard configuration to create something new.
A strategy can generate a single view, or a full dashboard including one or several views.

A strategy can be applied to the whole configuration or to a specific view.
As with custom cards, strategies need to be loaded as resources. Refer to [registering resources](./registering-resources.md) for more information on how to include strategies as resources.

Strategies are defined as a custom element in a JavaScript file, and included [via dashboard resources](./registering-resources.md). Home Assistant will call static functions on the class instead of rendering it as a custom element.
If you have already built a custom card, the setup will feel familiar. Strategies are loaded the same way, but instead of rendering a single card directly they generate dashboard content. They can also work alongside custom cards when you need them to. You can import custom cards into your strategy as with any other resource/element.

## Dashboard strategies
## Dashboards

A dashboard strategy is responsible for generating a full dashboard configuration. This can either be from scratch, or based on an existing dashboard configuration that is passed in.
A dashboard strategy generates a full dashboard configuration. In most cases, it starts from a small strategy config and returns the full dashboard structure. Think of this like a json/yaml config which is then rendered into a dashboard. Built in dashboards are built with dashboard strategies. You can read the source code for the built in dashboards [here](https://github.com/home-assistant/frontend/tree/dev/src/panels/lovelace/strategies).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Fix hyphenation and link text.

Two issues in this sentence:

  1. "Built in dashboards" should be hyphenated as "built-in dashboards" when used as an adjective before a noun.
  2. The link text "here" is non-descriptive. As per coding guidelines, apply the Microsoft Style Guide which recommends descriptive link text.
📝 Proposed fix
-A dashboard strategy generates a full dashboard configuration. In most cases, it starts from a small strategy config and returns the full dashboard structure. Think of this like a json/yaml config which is then rendered into a dashboard. Built in dashboards are built with dashboard strategies. You can read the source code for the built in dashboards [here](https://github.com/home-assistant/frontend/tree/dev/src/panels/lovelace/strategies).
+A dashboard strategy generates a full dashboard configuration. In most cases, it starts from a small strategy config and returns the full dashboard structure. Think of this like a json/yaml config which is then rendered into a dashboard. Built-in dashboards are built with dashboard strategies. You can read the [source code for the built-in dashboards](https://github.com/home-assistant/frontend/tree/dev/src/panels/lovelace/strategies).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
A dashboard strategy generates a full dashboard configuration. In most cases, it starts from a small strategy config and returns the full dashboard structure. Think of this like a json/yaml config which is then rendered into a dashboard. Built in dashboards are built with dashboard strategies. You can read the source code for the built in dashboards [here](https://github.com/home-assistant/frontend/tree/dev/src/panels/lovelace/strategies).
A dashboard strategy generates a full dashboard configuration. In most cases, it starts from a small strategy config and returns the full dashboard structure. Think of this like a json/yaml config which is then rendered into a dashboard. Built-in dashboards are built with dashboard strategies. You can read the [source code for the built-in dashboards](https://github.com/home-assistant/frontend/tree/dev/src/panels/lovelace/strategies).
🧰 Tools
🪛 LanguageTool

[grammar] ~17-~17: Use a hyphen to join words.
Context: ...is then rendered into a dashboard. Built in dashboards are built with dashboard s...

(QB_NEW_EN_HYPHEN)


[grammar] ~17-~17: Use a hyphen to join words.
Context: ...u can read the source code for the built in dashboards [here](https://github.com/...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.22.0)

[warning] 17-17: Link text should be descriptive

(MD059, descriptive-link-text)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/frontend/custom-ui/custom-strategy.md` at line 17, Update the sentence
to hyphenate the adjective and replace the non-descriptive link text: change
"Built in dashboards are built with dashboard strategies." to "Built-in
dashboards are built with dashboard strategies." and replace the link text
"here" with a descriptive phrase such as "built-in dashboard strategies source
code" (keeping the existing URL) so the link is meaningful and the adjective is
correctly hyphenated.


Two parameters are passed to the strategy:
### Show your community dashboard in the new dashboard dialog

| Key | Description
| -- | --
| `config` | Dashboard strategy configuration.
| `hass` | The Home Assistant object.
__Introduced in Home Assistant 2026.4.__

```ts
class StrategyDemo {
If you already have a dashboard strategy, you can make it much easier for people to add by registering your strategy in `window.customStrategies`.

Once the resource is loaded, Home Assistant can show your dashboard in the **new dashboard** dialog under the **Community dashboards** section.

The object supports the following keys:

| Key | Required | Description |
| ------------------ | -------- | ------------------------------------------------------------------------------ |
| `type` | Yes | The strategy type without the `custom:` prefix. |
| `strategyType` | Yes | The strategy kind: `dashboard`, `view`, or `section`. |
| `name` | No | Friendly name shown in the picker. |
| `description` | No | Short text shown below the name. |
| `documentationURL` | No | Link to your documentation. This is not shown in the strategy UI yet. |
Comment thread
timmo001 marked this conversation as resolved.
| `images` | No | Preview image, either a single URL or a light/dark object. |

Example with preview images:

```js
window.customStrategies = window.customStrategies || [];

window.customStrategies.push({
type: "my-demo",
strategyType: "dashboard",
name: "My demo dashboard",
description: "A small starter dashboard generated from JavaScript.",
documentationURL: "https://example.com/my-demo-dashboard",
// images: "/local/my-demo/preview.svg"
images: {
light: "/local/my-demo/preview-light.svg",
dark: "/local/my-demo/preview-dark.svg",
},
});
```

### Examples

A good example to start from is the [home overview](https://github.com/home-assistant/frontend/tree/dev/src/panels/lovelace/strategies/home) dashboard or the [energy dashboard](https://github.com/home-assistant/frontend/tree/dev/src/panels/lovelace/strategies/energy).

Or, for less complex example, the [map dashboard strategy](https://github.com/home-assistant/frontend/blob/dev/src/panels/lovelace/strategies/map/map-dashboard-strategy.ts) which imports the map view strategy. This in term uses a panel view type, which uses a single map card.
Comment thread
timmo001 marked this conversation as resolved.
Outdated

#### Basic example

This example is meant to be copied into a JavaScript file and loaded as a module resource. It includes both the strategy registration and the metadata needed to show it in the new dashboard dialog.

It is a good starting point, but we recommend using the [ReactiveElement](https://lit.dev/docs/framework/concepts/reactive-element/) class from Lit instead of HTMLElement and use types whether with TypeScript or JSDoc. See the [frontend repo](https://github.com/home-assistant/frontend/tree/dev/src/panels/lovelace/strategies) for full code examples.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

```js
class MyDemoDashboardStrategy extends HTMLElement {
static async generate(config, hass) {
const title = config.title || "My demo dashboard";
const locationName = hass.config.location_name || "Home Assistant";

return {
title: "Generated Dashboard",
title,
views: [
{
"cards": [
title: "Home",
path: "home",
cards: [
{
"type": "markdown",
"content": `Generated at ${(new Date).toLocaleString()}`
}
]
}
]
type: "markdown",
content:
`# ${locationName}\n\n` +
"This dashboard was generated by a community dashboard strategy.",
},
{
type: "entities",
entities: ["sun.sun"],
},
],
},
],
};
}
}

customElements.define("ll-strategy-my-demo", StrategyDemo);
customElements.define("ll-strategy-dashboard-my-demo", MyDemoDashboardStrategy);
Comment thread
timmo001 marked this conversation as resolved.

window.customStrategies = window.customStrategies || [];

if (
!window.customStrategies.some(
(strategy) =>
strategy.type === "my-demo" && strategy.strategyType === "dashboard",
)
) {
window.customStrategies.push({
type: "my-demo",
strategyType: "dashboard",
name: "My demo dashboard",
description: "A small starter dashboard generated from JavaScript.",
documentationURL:
"https://developers.home-assistant.io/docs/frontend/custom-ui/custom-strategy",
// images: "/local/my-demo/preview.svg" // single image
images: {
light: "/local/my-demo/preview-light.svg",
dark: "/local/my-demo/preview-dark.svg",
},
});
}
```

Use the following dashboard configuration to use this strategy:
Expand All @@ -52,96 +129,71 @@ strategy:
type: custom:my-demo
```

## View strategies
## Views

A view strategy is responsible for generating the configuration of a specific dashboard view. The strategy is invoked when the user opens the specific view.
A view strategy generates the configuration of a specific dashboard view. These can be reused in dashboard strategies if needed, as can custom cards be used in view strategies.

Two parameters are passed to the strategy:

| Key | Description
| -- | --
| `config` | View strategy configuration.
| `hass` | The Home Assistant object.

```ts
class StrategyDemo {
static async generate(config, hass) {
return {
"cards": [
{
"type": "markdown",
"content": `Generated at ${(new Date).toLocaleString()}`
}
]
};
}
}

customElements.define("ll-strategy-my-demo", StrategyDemo);
```
> This section is about view strategies. If you want to build a custom view layout element instead, refer to [custom views](./custom-view.md).

Use the following dashboard configuration to use this strategy:

```yaml
views:
- strategy:
type: custom:my-demo
- strategy:
type: custom:my-demo
title: Generated view
```

## Full example
## Full strategy example

It's recommended for a dashboard strategy to leave as much work to be done to the view strategies. That way the dashboard will show up for the user as fast as possible. This can be done by having the dashboard generate a configuration with views that rely on its own strategy.
It is often a good idea to let a dashboard strategy create the list of views, and let a view strategy generate the cards for each view. That keeps the first dashboard load smaller and lets Home Assistant build each view only when it is opened.

Below example will create a view per area, with each view showing all entities in that area in a grid.
The example below creates one view per area. Each generated view shows the entities for that area in a grid.

```ts
class StrategyDashboardDemo {
```js
class MyAreaDashboardStrategy extends HTMLElement {
static async generate(config, hass) {
// Query all data we need. We will make it available to views by storing it in strategy options.
// Query all the data we need. We will make it available to views by storing it in strategy options.
const [areas, devices, entities] = await Promise.all([
hass.callWS({ type: "config/area_registry/list" }),
hass.callWS({ type: "config/device_registry/list" }),
hass.callWS({ type: "config/entity_registry/list" }),
]);

// Each view is based on a strategy so we delay rendering until it's opened
return {
title: config.title || "Area dashboard",
views: areas.map((area) => ({
strategy: {
type: "custom:my-demo",
area,
devices,
entities,
},
title: area.name,
path: area.area_id,
strategy: {
type: "custom:my-area-dashboard",
area: area,
devices: devices,
entities: entities,
},
})),
};
}
}

class StrategyViewDemo {
class MyAreaViewStrategy extends HTMLElement {
static async generate(config, hass) {
const { area, devices, entities } = config;

const areaDevices = new Set();

// Find all devices linked to this area
for (const device of devices) {
if (device.area_id === area.area_id) {
// Find all devices in this area.
for (const device of config.devices) {
if (device.area_id === config.area.area_id) {
areaDevices.add(device.id);
}
}

const cards = [];

// Find all entities directly linked to this area
// or linked to a device linked to this area.
for (const entity of entities) {
// Find all entities in this area or belonging to a device in this area.
for (const entity of config.entities) {
if (
entity.area_id
? entity.area_id === area.area_id
: areaDevices.has(entity.device_id)
entity.area_id === config.area.area_id ||
(!entity.area_id && areaDevices.has(entity.device_id))
) {
cards.push({
type: "button",
Expand All @@ -161,13 +213,24 @@ class StrategyViewDemo {
}
}

customElements.define("ll-strategy-dashboard-my-demo", StrategyDashboardDemo);
customElements.define("ll-strategy-view-my-demo", StrategyViewDemo);
if (!customElements.get("ll-strategy-dashboard-my-area-dashboard")) {
customElements.define(
"ll-strategy-dashboard-my-area-dashboard",
MyAreaDashboardStrategy,
);
}

if (!customElements.get("ll-strategy-view-my-area-dashboard")) {
customElements.define(
"ll-strategy-view-my-area-dashboard",
MyAreaViewStrategy,
);
}
```

Use the following dashboard configuration to use this strategy:

```yaml
strategy:
type: custom:my-demo
type: custom:my-area-dashboard
```
20 changes: 14 additions & 6 deletions docs/frontend/custom-ui/custom-view.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "Custom view layout"
title: "Custom views"
---

By default Home Assistant will try to show the cards in a masonry layout (like Pinterest). A custom view layout allows developers to override this and define the layout mechanism (like a grid).
Expand Down Expand Up @@ -28,21 +28,25 @@ Cards and Badges will be created and maintained by the core code and given to th
(note: this example does not have all of the properties but the necessities to show the example)

```js
import { LitElement, html } from "https://unpkg.com/@polymer/lit-element@^0.6.1/lit-element.js?module";
import {
LitElement,
html,
} from "https://unpkg.com/@polymer/lit-element@^0.6.1/lit-element.js?module";

class MyNewView extends LitElement {
setConfig(_config) {}

static get properties() {
return {
cards: {type: Array, attribute: false}
cards: { type: Array, attribute: false },
};
}

render() {
if(!this.cards) {
if (!this.cards) {
return html``;
}

return html`${this.cards.map((card) => html`<div>${card}</div>`)}`;
}
}
Expand Down Expand Up @@ -95,6 +99,10 @@ Detail: none
To call an event, you can use:

```js
// Delete 4th card in the current view
this.dispatchEvent(new CustomEvent("ll-edit-card", { detail: { path: [3] } })) // this refers to the card element
// Delete 4th card in the current view (this refers to the card element)
this.dispatchEvent(
new CustomEvent("ll-edit-card", {
detail: { path: [3] },
}),
);
```
4 changes: 4 additions & 0 deletions docs/frontend/custom-ui/registering-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ resources:
- url: /local/<name of the resource>.js
type: module
```

If you are building a community dashboard, the resource must be loaded before Home Assistant can show it in the new dashboard dialog. After adding or updating the resource, refresh Home Assistant and reopen the dialog.

For the full dashboard strategy and picker registration flow, refer to [custom strategies](./custom-strategy.md).
Loading