Home Assistant is one of the best pieces of software I've ever used, but like all software, sooner or later an update tends to break things. For me, that happened when Home Assistant rolled out system-wide and user-level default dashboards in the 2025.12 release. It was a great update for someone running a simple, single-user setup, but for someone like me who's running multiple dashboards on multiple devices, it was a nightmare.
This update took away the ability to set a different default dashboard for each device. Creating different user accounts for different devices didn't make much sense for me, so I went digging. And now, I've finally fixed Home Assistant's most annoying dashboard problem.
A community script that looked perfect on paper
Local storage was the whole trick, until it wasn't
I first found a workaround on the Home Assistant community forum that looked promising on paper. It was a custom sidebar panel: a JavaScript file dropped into config/www/dashboard/device-default-dashboard.js and registered through panel_custom in the Home Assistant configuration.
It adds a sidebar entry with a dropdown listing all your dashboards. You pick one, and the script saves that choice to the browser's local storage while also clearing the newer server-side default settings, so HA falls back to the older, per-browser method of remembering your default view. The legacy local storage fallback is still alive in HA's current version, which is why this approach can work in the first place. The idea is simple: instead of fighting the new system-wide default, you sidestep it and let each browser remember its own preference, much like HA did before the 2025.12 update.
It sounds great on paper, except the fix I found was flawed, with four individual bugs, and did not work as expected out of the box. Hence began the bug hunting.
Every fix I made was hiding the next problem
Stale defaults, dead elements, and a silent dropdown
The first problem was a stale system-wide default that never got cleared. A quick check of HA's frontend data showed that the default panel was still set to my user account's default dashboard from before I even touched the script. HA checks that system-level value before it ever looks at the per-device local storage fallback, so no matter what the panel did on the frontend, that leftover setting was always going to win.
The second issue was in the panel's bootstrap code. The script grabbed LitElement by reading the prototype of customElements.get("ha-panel-lovelace"), but that element only gets registered once you have actually opened a real Lovelace dashboard in that browser session. Navigate straight to your custom panel from a fresh tab, and that element just doesn't exist yet. The result was a blank page and a browser console full of JavaScript TypeErrors.
Next, and more sneaky, was that ha-select doesn't work the way it used to. HA rewrote the component to recognize only its own auto-generated dropdown items, which now fire a wa-select event tied to a new Web Awesome component under the hood instead of the old and manually built
Finally, after everything was fixed, my browser kept running the broken version of the script. Home Assistant serves everything under /local/ with aggressive caching, saving data up to 31 days. Dynamic import of ES modules also does not reliably respect a hard refresh the way a normal script tag does. This meant that every fix I made was effectively invisible until I dealt with caching directly.
Four bugs, four fixes, one working dashboard
Chasing down home-assistant-main and a cache-busting query string
Getting this working meant tackling each bug individually instead of assuming one patch would cascade through the rest. First I cleared the leftover default_panel value with a direct WebSocket call to frontend/set_system_data, then verified it was gone by reading the system data again.
For the bootstrap issue, I changed the reference so LitElement gets pulled from home-assistant-main instead of ha-panel-lovelace. Home-assistant-main is guaranteed to exist as part of the app shell regardless of how you navigate to the panel, so the script can initialize even when you open it directly.
The dropdown needed a bigger rewrite. Instead of manually rendering items, I passed the available dashboards through ha-select's own .options property. That lets the component generate its correctly wired items internally. I also read the chosen value through ev.detail.value — the same approach Home Assistant's official dashboard picker uses.
For the caching problem, I appended a ?v=2 suffix to the module_url entry in the configuration.yaml file and restarted Home Assistant. This made sure that every device on the network requested a new URL, instead of loading the same stale module it had cached before.
If you want to implement these changes on your own HA instance, I've created a GitHub repository with the modified dashboard picker JavaScript, a configuration snippet, and a detailed breakdown of other gotchas you might come across. It's a far simpler solution than it seems, and for most people, just copying the device-default-dashboard.js file and adding the configuration snippet should do the job. You can also install it via HACS if that's what you prefer.
A frontend rewrite away from breaking again
Small bugs, but the kind that mask each other
None of these four bugs were especially difficult to fix. They're exactly the kind of issues you run into when a fast-moving open-source frontend restructures components between releases and community scripts inevitably lag behind. What made this frustrating wasn't the complexity of an individual fix, but the fact that each bug was masking the next, so the failure always appeared to be caused by something else.
Now every device in my house boots straight to the dashboard I actually want to see there. That said, this solution is still a workaround rather than an official feature, and future frontend changes can break it again. Regardless, it's a small quality-of-life fix, but it restores the kind of device-specific flexibility that made Home Assistant feel personal in the first place.