ESPHome, Composed
Build touchscreen UIs and device configs with TSX — reusable components, real data binding, compiled to standard YAML.
Read the DocumentationWhy ESPCompose?
Component-Based LVGL UIs
Build touchscreen interfaces from reusable components — Screen, Card, HStack, Button, and more.
Hot Reload, No Flashing
Iterate on your UI instantly — changes hot-reload in seconds without waiting for C++ compilation or device flashing.
Home Assistant Data Binding
Bind HA entities to UI with useHAEntity. Toggle lights, read state, control devices — no lambdas.
Type-Safe Everything
Auto-generated types for every ESPHome platform and UI component. Your IDE catches errors at build time.
Theming & Layout
Built-in dark and light themes, flex layouts, and a design system you can customize or extend.
Live Home Assistant Data
Preview your UI locally with `--host` and test with real Home Assistant entities, real state, real interactions.
Targets ESPHome Directly
Generates standard ESPHome YAML and triggers the compiler automatically — seamless from code to device.
Built on Node.js
Tap into TypeScript and the entire NPM ecosystem — use any package or tooling to power your build pipeline.
Open Source
Fully open source and community-driven. Inspect the code, contribute features, and shape the project's future.
Before & After
lvgl:widgets:- obj:layout: flexflex_flow: COLUMNchildren:- label:text: "Dashboard"- obj:layout: flexchildren:- button:id: office_btnon_press:- homeassistant.action:action: light.toggledata:entity_id: light.officechildren:- label:id: office_btn_labeltext: "Office"- button:id: gym_btnon_press:- homeassistant.action:action: light.toggledata:entity_id: light.gymchildren:- label:id: gym_btn_labeltext: "Gym"binary_sensor:- platform: homeassistantentity_id: light.officeon_state:- lvgl.label.update:id: office_btn_labeltext: !lambda |-if (x) return "Office On";return "Office Off";- platform: homeassistantentity_id: light.gymon_state:- lvgl.label.update:id: gym_btn_labeltext: !lambda |-if (x) return "Gym On";return "Gym Off";
import { useHAEntity } from "@espcompose/core";import { Screen, VStack, Card, HStack, Button, Text } from "@espcompose/ui";const HALight = ({ entity, text }) => {const light = useHAEntity(entity);return (<Buttontext={light.isOn ? `${text} On` : `${text} Off`}onPress={() => { light.toggle(); }}/>);};export const Dashboard = ({ display }) => (<lvgl displays={[display]}><Screen><VStack><Text variant="title" text="Dashboard" /><Card><HStack><HALight entity="light.office" text="Office" /><HALight entity="light.gym" text="Gym" /></HStack></Card></VStack></Screen></lvgl>);
A New System, Get Involved
ESPCompose is a new system that is evolving quickly and shaped by the community. Report bugs, suggest features, contribute code, and help guide what comes next.
Support ESPCompose
If ESPCompose saves you time or helps your projects ship faster, a small coffee keeps development moving. Your support funds new features, documentation, and maintenance.
