A modern tabbed card for Home Assistant Lovelace dashboards — organize multiple cards into clean, themeable tabs with a visual editor, conditional tabs, badges, and persistent / deep-linkable selection.
Built from the ground up in Lit with zero external UI-component dependencies, so it does not collide with Home Assistant's own components (a common source of breakage in older tabbed cards after HA frontend updates).
A nested map card rendering correctly the moment its tab is selected — no "navigate away and back" workaround needed:
🎮 Try the live demo: edit a config and flip pretend devices in your browser, no Home Assistant needed.
📖 Full documentation, every option, and screenshots are on the Wiki. This README is a quick overview.
- Get started fast — starter templates (Rooms, Lights, Security, Media, Climate, Energy, Family) built from your own entities, and convert from another card (tabbed-card, swipe-card, state-switch, Bubble pop-ups, stacks, sections views, whole dashboards).
- Visual GUI editor — add, remove, drag-to-reorder, duplicate, copy & import tabs without YAML; a live "why is this tab hidden?" check of every condition; collapsible tab blocks, native HA card editor, HA's native condition builder for visibility/alert/default, style presets, duplicate-name warnings.
- Keep-alive content (or opt-in
unmount_hidden) — maps/cameras/graphs render correctly without navigating away. - Tab bar styling —
top/bottom/left/rightpositions;underline/pill/segmented/boxed/textstyles;tab_display(icon/label/both/active/dots);align;indicator_size;sticky;elevation;bar_background; per-tabaccent/color. - Per-tab —
subtitle,badge(text ordot,badge_color,badge_format, hide-inactive),disabled, multiplecards/columns, per-tabstyles;icon/name/colorcan be live templates; pulsingalertconditions. - Actions —
tap_actionturns a tab into a navigation link/button;hold_action,badge_action, andenter_action/leave_actionwhen a tab opens or closes (incl.perform-action). - Edit-mode helpers — hidden tabs appear faded while you edit the dashboard; rotation pauses.
- Dynamic — conditional
visibility(state/numeric_state/screen/time/user/template+and/or/not),auto_selecton entity state, conditionaldefault_if, generated tabs viaauto_tabs(Jinja, or one tab per area/label). - Layouts —
scroll_spyanchor mode,collapsibledeck, responsivesplitview (side by side on wide cards) andtab_display_narrow. - Content —
layout: masonryorlayout: sections(like HA's sections view) inside a tab,header_cards/footer_cardson every tab, per-tabbackground(colour, gradient or image, may be a template),content_height: tallestso the page doesn't jump, afullscreenbutton, andswipe_peekcarousel swiping. - Wall panels —
auto_rotatethrough tabs,idle_returnto the default tab, automations switch tabs (remote_entity,remote_event) and hear tab changes (fire_event), starting tab by time of day or device, PIN-locked tabs. - Smart tabs — a tab per floor, domain, device, to-do list, calendar, camera or zone; area tabs that fill themselves with tiles; count badges without Jinja, badge icons and progress rings; alerting tabs first, with long-press snooze.
- Persistence — remember the active tab per
browser,url(#tab=name, slug or index), or across devices via anentitywith live two-way sync. - Transitions — optional
fade/slide/scale/flip/risepanel animations with your own duration and easing (respects reduced-motion). - Theming — CSS variables via
styles, no card-mod needed. - Power users —
tab_defaultsfor every tab, reusabletab_templates(use+vars), adebugpanel, and a public API (selectTab(),tabdeck-tab-changed). - Accessible —
tablistsemantics, full keyboard navigation (Arrows, Home/End, skips disabled). - Translated — editor and card text in English, Deutsch, Español, Français, Italiano, Nederlands, Polski and Português, following your HA language. Help translate.
Tabdeck Card is in the default HACS store — no custom repository needed.
- In HACS, search for Tabdeck Card and open it.
- Click Download.
- HACS registers the resource automatically at
/hacsfiles/tabdeck-card/tabdeck-card.js(type: JavaScript Module).
- Download
tabdeck-card.jsandtabdeck-card-editor-….jsfrom the latest release. - Copy both to
config/www/(side by side; remove an oldertabdeck-card-editor-….js). - Add a dashboard resource: Settings → Dashboards → ⋮ → Resources → Add,
URL
/local/tabdeck-card.js, type JavaScript Module. (Only the card is a resource; it loads the editor file itself when you open the visual editor.)
type: custom:tabdeck-card
default_tab: Lights
position: top
style: underline
remember: url
tabs:
- name: Lights
icon: mdi:lightbulb
accent: "#ffcc00"
card:
type: light
entity: light.kitchen
- name: Climate
icon: mdi:thermostat
badge: sensor.living_room_temperature
card:
type: thermostat
entity: climate.living_room
- name: Guests
icon: mdi:account-group
visibility:
- condition: state
entity: input_boolean.guest_mode
state: "on"
card:
type: entities
entities:
- light.guest_roomTry it in the live demo, browse other people's decks on the Showcase, and share yours.
A ready-to-paste dashboard showcasing many features lives in
examples/demo.yaml.
The tables below cover the essentials. See the Configuration wiki page for the complete, always-current reference (every option links to a feature page).
| Option | Type | Default | Description |
|---|---|---|---|
type |
string | — | custom:tabdeck-card (required). |
tabs |
list | — | One or more tab objects (required). |
default_tab |
number | string | 0 |
Index or tab name shown first. Overridden by persistence. |
position |
string | top |
top | bottom | left | right. |
style |
string | underline |
underline | pill | segmented | boxed | text. |
tab_display |
string | both |
both | icon | label | active (icons, plus name + subtitle on the selected tab). |
align |
string | start |
start | center | end | justify. |
indicator_size |
number | 3 |
Underline thickness (1–16 px). |
accent_indicator |
boolean | true |
Colour the indicator by the selected tab's accent. |
badge_display |
string | text |
text | dot. |
hide_inactive_badge |
boolean | false |
Hide 0/off badges. |
transition |
string | none |
Panel switch animation: none | fade | slide | scale | flip | rise (+ transition_duration, transition_easing, transition_narrow for phones). |
sticky / elevation |
boolean | false |
Pin the bar / raise it with a shadow. |
bar_background |
string | — | Custom tab-bar background colour. |
header |
boolean | false |
Show the active tab's title above the content. |
unmount_hidden |
boolean | false |
Keep only the active tab's card in the DOM. |
unload_after |
number | — | Free tabs not opened for this many minutes (rebuilt on return). |
header_cards / footer_cards |
list | — | Cards above the bar / below the content on every tab. |
content_height |
string | number | auto |
auto | tallest (no page jump) | a fixed height; content_max_height scrolls inside. |
fullscreen |
boolean | false |
Full-screen button: open the active tab over the whole window. |
swipe_peek |
boolean | false |
Content follows the finger while swiping; the next tab slides in. |
alert_first / alert_snooze |
boolean / number | false / — |
Alerting tabs first; long-press snoozes an alert for N minutes. |
remote_entity |
string | — | Switch to the tab named by this entity's state (automations, wall panels). |
remote_event / fire_event |
boolean | false |
Switch on tabdeck_select events / fire tabdeck_tab_changed (admin users). |
default_by_time / default_by_device |
object | — | Starting tab by time of day / by browser (?tabdeck_device=). |
scrollable |
auto | boolean |
auto |
Scroll the tab bar when tabs overflow. |
remember |
string | none |
none | browser | url | entity (+ remember_entity, storage_key). |
swipe_wrap |
boolean | false |
Swipe wraps around the ends. |
lazy |
boolean | false |
Create inactive tab cards on first visit instead of up front. |
animated |
boolean | true |
Slide the active-tab indicator between tabs. Snaps instantly when false or under reduced-motion. |
swipe |
boolean | false |
Change tabs by swiping left/right on the card body (touch devices). Off by default so it never hijacks gestures of interactive cards (maps, sliders). |
styles |
object | {} |
CSS-variable overrides (see Theming). |
tab_defaults / tab_templates |
object | — | Keys for every tab / named tab blueprints (use + vars on a tab). |
debug |
boolean | false |
Panel with conditions, templates, start reason and timings. |
preset |
string | — | default | ios | material | glass | minimal | rail style bundle; explicit keys win. |
badge_format |
object | — | Default numeric badge format {precision, unit, max}. |
auto_rotate / idle_return |
number | — | Cycle tabs every N s / return to the default tab after N s idle. |
scroll_spy |
boolean | false |
Stack all tabs as sections; the bar scrolls to and tracks them. |
collapsible / start_collapsed |
boolean | false |
Tap the active tab to fold the content away. |
split |
number | object | — | Side-by-side columns at/above this card width ({min_width, columns}). |
tab_display_narrow / narrow_width |
string / number | — / 450 |
Display mode on narrow cards. |
| Option | Type | Default | Description |
|---|---|---|---|
name |
string | — | Tab label; also the id used by default_tab and #tab=. |
subtitle |
string | — | Secondary text under the label. |
icon |
string | — | Optional mdi: icon. |
accent |
string | — | Per-tab accent (indicator + selected state). |
color |
string | — | Fixed label/icon colour for the tab. |
badge / badge_color |
string | — | Entity id or Jinja template; optional badge colour. |
disabled |
boolean | false |
Show greyed-out and non-selectable. |
hold_action |
action | — | HA action fired on long-press (tap still selects). |
tap_action |
action | — | Tap runs this action instead of opening the tab (navigation tabs; no card needed). |
enter_action / leave_action |
action | — | HA action when the tab becomes / stops being active. |
badge_format |
object | — | {precision, unit, max} numeric badge formatting. |
alert |
list | — | Conditions that make the tab pulse while met. |
styles |
object | — | CSS properties applied while this tab is active. |
auto_select |
string | object | — | Switch to this tab when an entity becomes active. |
default_if |
list | — | Conditions that make this the default tab on load. |
visibility |
list | — | Conditions (see below); the tab is hidden when unmet. |
card / cards / columns |
object/list/number | — | A single card, or multiple cards (optionally in a columns grid). |
layout |
string | — | masonry: balance cards into columns (or min_column_width px). sections: titled sections like HA's sections view (max_columns, per-section column_span). |
background |
string | — | Content background while open: colour, gradient or image URL (may be a template). |
area / floor / label |
string | — | No card needed: tiles of the entities there (domains filters). |
badge_icon / ring |
string | — | Icon badge; progress ring around the icon (ring_max, ring_color). |
pin |
string | — | Ask for a code before the tab opens (convenience lock, not security). |
Supported condition types: state, numeric_state, screen, time, user, template, and and/or/not groups (nestable). See the Tab Visibility wiki page.
visibility:
- condition: state
entity: input_boolean.guest_mode
state: "on"
- condition: numeric_state
entity: sensor.temperature
above: 18
below: 26
- condition: template
value_template: "{{ is_state('alarm_control_panel.home', 'armed_away') }}"Both badge and template visibility conditions accept Jinja templates,
rendered server-side by Home Assistant and updated live. A tab with a
template visibility condition stays hidden until the template first renders
truthy (and is hidden again if the template errors).
The card inherits your Home Assistant theme by default. Fine-tune via CSS
variables (set globally in your theme, or per-card under styles):
| Variable | Purpose |
|---|---|
--tabdeck-accent |
Active tab / indicator color. |
--tabdeck-tab-height |
Height of the tab bar. |
--tabdeck-tab-font-size |
Tab label font size. |
The visual editor lets you manage tabs (add / delete / drag-to-reorder / duplicate), with collapsible per-tab blocks and a card-type chooser that drills into Home Assistant's native card editor. Each tab exposes name, subtitle, icon, accent, colour, badge and long-press action; global controls cover every top-level option. It warns about footguns like duplicate tab names. See the Editor wiki page.
The editor's Convert from another card button turns a pasted tabbed-card,
swipe-card, state-switch, Bubble Card pop-ups, a stack, a sections view or a
whole dashboard into Tabdeck tabs. See
Convert from another card.
Tabdeck also reads several of the original kinghat/tabbed-card keys so existing
configs keep working:
options.defaultTabIndex→default_tab- per-tab
attributes.label→name,attributes.icon→icon
Update to the new keys when convenient; the new keys win if both are present.
MIT © 2026 John Mackinnon

