-
-
Notifications
You must be signed in to change notification settings - Fork 0
Repeating a Template
A template removes the repetition inside a card. for_each removes the repetition
around it — one card renders the template once per item in a list, instead of you pasting
the same instance block out four times.
type: custom:decluttering-card-plus
template: room_tile
columns: 2
variables:
- colour: amber
for_each:
- entity: light.living_room
name: Living Room
- entity: light.kitchen
name: Kitchen
colour: blue
- entity: light.bedroom
name: Bedroom
- entity: light.hallway
name: HallwayFour tiles, two columns, from one card.

Kitchen sets its own colour; the other three take the one set on the card.
An item is a set of values for one copy. It can carry a nested mapping, and each piece of it can be reached on its own (v1.2.0+) — which is what lets one item hold a room's light, its sensor and its icon together:
for_each:
- room: { name: Hall, light: light.hall, icon: 'mdi:lamp' }content: [[room.name]] uses [[room.light]]' # and [[room]] is still the whole mappingThree levels deep. A list is reached whole or not at all.
Each item holds the variables for that copy, written as a mapping — which reads better than a list of one-key entries when every copy sets the same few things:
for_each:
- entity: light.kitchen
name: KitchenA list of one-key entries works too, if you prefer it to match variables::
for_each:
- - entity: light.kitchen
- name: KitchenA for_each that is a single mapping rather than a list counts as a list of one, the same
forgiveness variables: gets — and an empty mapping counts as no list at all, so a cleared
field never leaves a stray single-copy stack behind.
Anything in the card's own variables: is shared by every copy, and an item can override
it. Above, colour is amber everywhere except the shed.
That is the usual shape: put what is the same in variables:, and what differs in
for_each.
Writing the list out means the dashboard stops being true the moment a lamp is added.
for_each_from asks Home Assistant instead, so the card grows by itself:
type: custom:decluttering-card-plus
template: room_light
columns: 2
for_each_from:
domain: light
area: KitchenRepeat over entities or over areas, and narrow by domain, area, floor or label.
Each copy is given the things it needs — the entity id, its name, the area it is in — and
the list is worked out again when the registry changes, not on every state change.
A written-out for_each wins over for_each_from if a card somehow has both: it is the
more particular of the two.
A sweep that takes everything is rarely what you want. These narrow it, and can be combined:
| Key | Narrows to | Matches by |
|---|---|---|
domain |
entities of a domain |
light, binary_sensor, … |
device_class |
entities reporting a device class |
motion, door, temperature, … |
integration |
entities from one integration |
hue, zha, mqtt, … |
area |
entities in an area | its name or its id |
floor |
entities on a floor | its name or its id |
label |
things carrying a label | its name or its id |
A domain on its own is usually too coarse — nobody wants motion, door and smoke sensors on the same card:
for_each_from:
domain: binary_sensor
device_class: motionA label put on a device counts for all of that device's entities, which is how labels are normally used.
for_each_from:
domain: light
exclude: light.bedside # a pattern, or a list of themWritten as patterns, exclude means entity ids — which is what it is nearly always for.
Written as a mapping it narrows by anything the source itself can:
for_each_from:
entities: '*'
exclude:
area: Bedroom
label: hiddenWhat is left out wins over what is taken in, so "all of these except those" is one line rather than a list of everything you did want.
for_each_from:
domain: light
sort: area # name (the default), entity, id, area, domain, floor
reverse: true
limit: 5sort: orders the copies; anything it does not recognise keeps the registry's own order,
which is also how you ask for "however Home Assistant listed them". reverse: turns
whichever order you chose around.
limit: caps how many copies are built. Every copy still carries total — what
matched before the limit — so a card can be honest about what it is not showing:
card:
type: markdown
content: 'Showing [[count]] of [[total]] lights'for_each_from:
range: 4Four copies, with nothing but the position to go on. Useful
for a row of slots, or for laying something out before the entities behind it exist.
Asking for range: 0 gives no copies — not, as you might fear, every entity in the house.
A repeat that matches nothing renders nothing. That is deliberate — a dashboard should not break because a room has no motion sensor yet — but silence looks the same as breakage, so a card can say so on purpose:
type: custom:decluttering-card-plus
template: motion_tile
for_each_from:
domain: binary_sensor
device_class: motion
empty:
type: markdown
content: No motion sensors set up yet.This is the shape people build most: a tile per room, each showing that room's lights.
with: gathers the entities for each area and hands them to the copy.

One card. Each copy is an area, and the tiles under it come from a second template repeated over that area's own entities.
# The outer card - one copy per area.
type: custom:decluttering-card-plus
template: room_summary
for_each_from:
areas: true
with:
domain: light
columns: 3
min_column_width: 240# The template, which repeats again over what its area holds.
type: custom:decluttering-template-plus
template: room_summary
card:
type: vertical-stack
cards:
- type: markdown
content: '### [[area]] — [[entity_count]] lights'
- type: custom:decluttering-card-plus
template: light_tile
for_each: '[[items]]'Each area copy is given, on top of the usual area values:
| Name | Is |
|---|---|
items |
The whole mappings, ready to hand to a nested for_each
|
entities |
Just the entity ids, as a list |
entity_count |
How many there are |
The area being grouped always wins over any area: inside with: — that is the point of
grouping.
An area with nothing in it is left out, because a card for a room with no lights is
noise rather than news. with: { keep_empty: true } keeps it, which is how you get to say
"nothing in here" on purpose.
Every copy is also given [[index]], counting from one, and [[count]], the number of
copies — so a template can number itself without you writing the number into each item:
card:
type: markdown
content: 'Room [[index]] of [[count]]'An item that sets index or count itself wins, so a template already using those names
for something else keeps working.
for_each skips an item that leaves a required: true variable empty, which is what lets
one template serve a room with four lights and a room with one:
type: custom:decluttering-template-plus
template: room_light
variables:
- name: entity
required: true
card:
type: tile
entity: '[[entity]]'type: custom:decluttering-card-plus
template: room_light
columns: 4
for_each:
- entity: light.kitchen
- entity: light.hall
- entity: ''
- {}That renders two cards, not four, with no gaps where the other two would have been. A
template that requires nothing renders every item as before. See
Describing Variables for required.
columns |
Result |
|---|---|
absent, or 1
|
The copies stack vertically |
2 or more |
The copies are laid out in that many columns |
with min_column_width
|
columns becomes the most it will use; a column is dropped rather than going narrower |
The copies are handed to Home Assistant's own vertical-stack and grid cards, so they
behave exactly like any other card in your layout — including in the
sections layout, and including
visibility conditions on each copy.
A repeat only works on a template that defines a card. Home Assistant gives a row, a badge or a picture element a single slot to fill, so there is nowhere for a second copy to go — put what you want repeated inside a card template, and repeat that.
for_each needs a template that defines a card. A row, badge or
element has nothing to stack, and asking for one is an error:
for_each needs a template that defines a card
An empty list renders nothing rather than failing the card, so a list you build up over time can start out empty without breaking the dashboard.
A style on the card resolves against the card's own variables and the
template's declared defaults, not any one item's values — per item it would be ambiguous.
For a card template, the card editor offers Repeat for each and Columns below the variables.
The missing-variable warning accounts for the items, so a card whose for_each supplies
entity and name is not told they are missing. See
Describing Variables.
If the copies differ by more than a few values — different card types, different
structure — you want separate cards, or a template with
visibility conditions inside it. for_each is for the case where the only
thing that changes is what you pass in.
| Key | Type | Description |
|---|---|---|
for_each |
list | One copy of the template per item; each item is that copy's variables. A single mapping counts as a list of one |
columns |
number | How many copies sit side by side. Defaults to 1 |
min_column_width (v1.1.0+)
|
number | Pixels. Drops a column rather than going narrower, so one card suits a phone too |
for_each_from (v1.1.0+)
|
mapping | What to repeat over, read from Home Assistant instead of written out |
empty (v1.2.0+)
|
card | What to render when the repeat produces no copies |
gap (v1.2.0+)
|
number | Pixels between the copies. Leave it out for Home Assistant's own spacing |
→ Using Templates · Describing Variables → Recipes for finished examples
Getting started
Core concepts
Content types
Features
- Repeating a Template
- Sharing Between Dashboards
- Sharing a Template
- Visibility
- Styling
- Visual Editors
- Translations
Reference