Use the hooks in your app
Put Dashhaus in a React app of yours: install the packages, connect the store, then draw one light twice. The first
uses your own markup on the headless hook; the second, a skin’s ready-made component. @dashhaus/core on its own is
enough for the first: no skin, no generator and no Lovelace card come with it. If you only want a finished
dashboard in Home Assistant, Install from the Marketplace needs no React at all.
1. Install
Section titled “1. Install”npm install @dashhaus/core react react-dom home-assistant-js-websocket@dashhaus/core has no markup and no CSS. React 18.3 or newer (19 included) is a peer.
home-assistant-js-websocket is only needed by @dashhaus/core/websocket, which opens the connection.
npm install @dashhaus/orb @dashhaus/core react react-dom home-assistant-js-websocket@dashhaus/orb brings @dashhaus/core and @dashhaus/dashboard with it; naming core lets your own code import
the store and the hooks. Importing @dashhaus/orb loads Orb’s stylesheet, so build with a bundler that handles
CSS imports, such as Vite.
npm install @dashhaus/field @dashhaus/core react react-dom home-assistant-js-websocketThe same as Orb: @dashhaus/field brings core and the dashboard package, and importing it loads Field’s
stylesheet.
2. Put the store above your app
Section titled “2. Put the store above your app”Everything reads Home Assistant through one store. createHaStore turns a transport into the store and HaProvider
connects it while it is mounted. Your app owns the login and the transport: here Home Assistant’s OAuth flow from
home-assistant-js-websocket, then connectHomeAssistant from @dashhaus/core/websocket.
import { createRoot } from "react-dom/client";import { getAuth } from "home-assistant-js-websocket";import { HaProvider, createHaStore } from "@dashhaus/core";import { connectHomeAssistant } from "@dashhaus/core/websocket";import { App } from "./App";
// Your app owns the login: here, Home Assistant's own OAuth flow through home-assistant-js-websocket.const auth = await getAuth({ hassUrl: "http://homeassistant.local:8123" });const transport = await connectHomeAssistant(auth);const store = createHaStore(transport);
createRoot(document.getElementById("root")!).render( <HaProvider store={store}> <App /> </HaProvider>,);No Home Assistant yet? createDemoStore() is the store on a demo house: the same entities Storybook and this
site’s live dashboards use, and every action answers.
import { createRoot } from "react-dom/client";import { HaProvider } from "@dashhaus/core";import { createDemoStore } from "@dashhaus/core/demo";import { App } from "./App";
// The demo house behind the real store: no Home Assistant needed, and every action answers.const store = createDemoStore();
createRoot(document.getElementById("root")!).render( <HaProvider store={store}> <App /> </HaProvider>,);import { LivingRoomLight } from "./LivingRoomLight";
export function App() { return ( <main> <h1>Home</h1> <LivingRoomLight /> </main> );}Inside Home Assistant, a skin’s Lovelace card does this for you on the frontend’s own connection; you never handle a token there. Connecting and auth covers both, and a kiosk.
3. Draw a first control
Section titled “3. Draw a first control”useHaLight hands over the light as a plain LightEntity, today’s history, and its actions. You draw it:
import { useHaLight } from "@dashhaus/core";
/** One light, drawn your way: the hook hands over the light and what it can do. */export function LivingRoomLight() { const { entity, actions } = useHaLight("light.living_room"); const on = entity.state === "on";
return ( <button type="button" aria-pressed={on} disabled={entity.state === "unavailable"} onClick={actions.onToggle}> {entity.name}: {on ? `on${entity.brightness == null ? "" : `, ${entity.brightness}%`}` : "off"} </button> );}A tap calls light.toggle. When Home Assistant reports the new state, the store updates and the button follows:
the hook never assumes the call worked.
HaLightTile is the same hook above Orb’s light tile: the round button, the brightness track, the colour and
warmth controls in its details sheet, and today’s history.
import { HaLightTile } from "@dashhaus/orb";
/** The same light as Orb draws it: the round button, the brightness track and its details sheet. */export function LivingRoomLightTile() { return <HaLightTile entityId="light.living_room" />;}A tile fills the width of its container and keeps a fixed height, its footprint. Put class="ha-surface" on
<body> for Orb’s page background and type.
HaLightModule is the same hook above Field’s light module: a key, a fader and a screen, with a detail panel.
import { HaLightModule } from "@dashhaus/field";
/** The same light as Field draws it: a key, a fader and a screen, with its detail panel. */export function LivingRoomLightModule() { return <HaLightModule entityId="light.living_room" />;}Put class="fd-surface" on <body> for Field’s page and type.
Every kind of entity has a hook, a headless component and a tile and a module, so the same swap works for any of them. Entities lists them all.
- Say when the dashboard can’t reach Home Assistant: Connection status.
- Draw a list of entities, every light in the house, with the headless components: Light.
- Test a connected control without Home Assistant: Testing and demo data.