I have been looking for an example where someone mounts a React component to a lovelace web component, but could not find any clean examples. So I made my own.
I have come up with a POC that mounts a React component to a web component’s shadow root.
The other issue is the repeated rendering when hass or config is changed. This can be solved very easily now with @preact/signals-react.
Pros:
React based Lovelace Card
Build the component with modern TS/TSX/JS/JSX
No need for HASS connectors - hass consumed directly from Home Assistant
Highly configurable
Use any other npm libraries
Cons:
Requires build step.
No direct pipelines (yet).
Can’t use Radix UI (because they use document.documentElement to deal with dark mode ).
I’d like to hear comments and thoughts about this!
I would like to know if you’ve found a way to live preview your card during development, similar to how we develop a classic React app. Perhaps simulate a config file outside of the Home Assistant instance? Also, if we update the .js file located in /local/www, how can we easily update the card?
I haven’t been working on this, got put on a major project this year so all my personal projects have been dragged down lately
That aside, I haven’t found a fix for the stale js file, but what HACS does is include a cache breaker hash for each imported resource. If you’re keen to make some progress, I’m guessing that will be the direction eventually.
As for having a local version of the script, you could mock the value of the hass and config signal and replace it wherever required. Since it’s a signal, it’s gonna work no matter what updates it.
You can find the signals in utilities/registerCard.ts, might have to export the signals individually.
Edit: @Nycco sorry not sure why the post wasn’t replied to your comment.
I managed to get past the cache problem using the browser’s inspector mode and a hard reload.
Regarding the local version, it’s quite easy to export the hass and config signal, but I don’t know how I can preview the project locally with the ‘npm run dev’ command, for example.
You’d need to mount the app’s custom element on a page.
Currently it’s being registered as a custom element in registerCard.ts so you could make use of that and register the app as a custom component, then just consume it in the body of a html file.
Will need some tweaking to make it happen.
TL;DR;
createReactCard.tsx - Creates a custom element.
registerCard.ts - Sets up signals and registers the custom element (see web component).
Load the bundle in a page using a script tag, then place <react-card> somewhere in your html file.
Last question do you think there’s a way to get the Material Design icon of HA into the custom card? To use the icon property in the YAML custom card config.
Home Assistant loads icons on the dashboard using the <ha-icon icon="mdi:something" class="icon" > custom element. You won’t be able to load the icons when testing locally, but you can definitely use them in the app and have them load on the dashboard. (I think, not sure if there are any caveats)
Love this! Thanks for building this out and documenting the process.
Don’t feel obligated to look into this if you aren’t sure because this is still definitely way better than the vanilla Javascript code I was writing for custom cards before. But, I use React Redux at work which to my understanding is nice for pulling state up globally so you don’t have to prop drill any of the state.
Anyway, I tried providing a store to the app in the createReactCard() utility but I don’t seem to actually have access to it when I run on HA. Curious if you know of any reason that couldn’t work on HA? I have it working in my hacky little local dev setup that bypasses the registerCard() calls entirely though.
If you (or anyone else discovering this) want to take a look, I made a public repo where you can look into the nonsense I’m trying. I’ll try to remember to update this comment if I get it working so I don’t send anyone on a wild goose chase.
UPDATE: I got it working sorta. I think my reducer setup is a little weird. I’ll try to commit something for other people’s reference when I have it cleaner.
Just checked out your repo, I would advise to inject your redux store into App.tsx rather than main so that it works both locally and when registered as a card on HA.
App.tsx is intended to be the entry point for your application, anything else above should be considered boilerplate.
I found this because it looked promising for integrating tldraw into Home Assistant, but it isn’t as easy as I’d hoped. Could the issues be related to the fact that tldraw uses Radix UI under the hood?
It could be, from what I remember (very vaguely), I had issues injecting styles from Radix. Functionality should not be affected if that’s the cause, but it would look pretty jarring.