Repository navigation
Placeholder Reference
Everything that can go inside [[ ]], one row each, with what it gives back. This page is
for looking something up: what a step is called, what it does with an empty value, whether
it gives a number or text. For why you would use one, and longer examples, follow the link
in each section to Variables.
Every example on this page has been run through the card. Where a result is quoted, as in
"5", it is text; unquoted numbers, true and false are real values.
Not sure what something does? Try a placeholder shows what each step of a chain gives out, in your browser, without touching a dashboard.
On this page: How a placeholder is written · Text · Text with an argument · Lists · Sums · When there is no value · Yes or no · Asking Home Assistant · List entries · What comes out: text or a real value · Which value wins · Limits
name: '[[room|trim|title|default:Somewhere]]'
# └─┬┘ └──────────┬─────────────────┘
# variable steps, run left to right| Form | What it does | Example | Result |
|---|---|---|---|
[[name]] |
The variable's value |
entity: '[[light]]' with light: light.hall
|
light.hall |
[[name|step]] (v1.1.0+)
|
The value, passed through one or more steps, left to right |
'[[room|slug|upper]]' with room: Back Garden
|
BACK_GARDEN |
[[name?]] (v1.1.0+)
|
Optional: with no value, the key it sits on is removed (see below) |
name: '[[label?]]' with nothing set |
the name key is gone |
[[!name]] (v1.1.0+)
|
Escaped: written out as the text [[name]], never substituted |
content: 'Type [[!entity]]' |
Type [[entity]] |
[[a.b]] (v1.2.0+)
|
A key inside a variable that holds a mapping, up to three keys down |
'[[room.light]]' with room: {light: light.hall}
|
light.hall |
[[a.chosen]] (v2.6.0+)
|
What a choose selector picked: the value under the key its active_choice names. A real chosen key wins |
'[[dims.chosen]]' with dims: {active_choice: Ratio, Ratio: '16:9'}
|
16:9 |
[[a.[[b]]]] (v1.15.1+)
|
A key picked by another variable: the inner placeholder is filled first |
[[colours.[[level]]|default:grey]]' with colours: {error: red}, level: error
|
red |
Rules that apply to every placeholder:
-
Steps run left to right, each one working on what the last one gave. A step that asks
Home Assistant reads its value as an entity id, so it goes before anything that reshapes it:
[[entity|friendly_name|upper]], not[[entity|upper|friendly_name]]. -
The
?goes last, after every step:[[room|slug?]]. -
No spaces round the bar.
[[room | slug]]is not a placeholder, and neither is one with a misspelt or upper-case step: the card leaves it exactly as written, so you can see it. -
An argument runs from the colon to the next
|or]]. It can hold spaces and colons (default:mdi:homeis fine), but never|,[or]written as text. There is no escape character. A whole placeholder can stand in as the argument (default:[[room]],add:[[offset]]), because it is filled in first. -
A
?written against the closing brackets is always the marker, never part of an argument:[[who|prefix:Mr ?]]is an optionalwhowithMrin front. -
Placeholders work in keys too, not only values:
'[[key]]': on. -
Name a variable anything except with
[,]or|in it, starting with!or ending with?. Spaces and dots are allowed, though a dot reads as a lookup into a mapping first. -
A placeholder nothing fills is left as written, and the browser console says which.
That is on purpose:
[[room]]showing on a card is far easier to spot than a blank. See Unresolved placeholders.
More: Writing placeholders, Value types.
Each of these does one fixed thing to text. A number or true/false is shaped as its
text, a null as the word null, and an empty value stays empty. A mapping or a list is
refused: the placeholder is left as written, because uppercasing JSON would only garble it.
The result is always text.
| Step | What it does | Example | Result |
|---|---|---|---|
slug |
Lower case; every run of anything but a letter or digit becomes one _, trimmed from the ends |
'light.[[room|slug]]' with room: John's Back Garden
|
light.john_s_back_garden |
kebab (v1.1.0+)
|
As slug, with -
|
'[[room|kebab]]' with room: Back Garden
|
back-garden |
upper |
Upper case |
'[[room|upper]]' with room: Hall
|
HALL |
lower |
Lower case |
'[[room|lower]]' with room: Hall
|
hall |
title |
First letter of every word upper case, the rest lower case |
'[[room|title]]' with room: back GARDEN
|
Back Garden |
capitalize (v1.3.0+)
|
First character upper case, everything else left alone |
'[[room|trim|capitalize]]' with room: ' living room'
|
Living room |
trim (v1.3.0+)
|
Whitespace removed from both ends |
'[[room|trim]]' with room: ' Hall '
|
Hall |
slug only keeps the letters a to z, so accented letters go: Café becomes caf. Order
matters in a chain: [[room|slug|upper]] gives JOHN_S_BACK_GARDEN, while
[[room|upper|slug]] gives john_s_back_garden, because slug lower-cases.
More: Asking for a variable in a different shape.
(v1.4.0+, except find:)
| Step | What it does | Example | Result |
|---|---|---|---|
replace:from:to |
Every from becomes to. Leave :to off to delete. from is everything up to the first colon, so it cannot be a colon itself |
'[[room|replace:_: ]]' with room: living_room_lamp
|
living room lamp |
prefix:text |
text in front. An empty value stays empty, so ? and default: still see a gap |
'[[room|prefix:light.]]' with room: hall
|
light.hall |
suffix:text |
text after; an empty value stays empty |
'[[n|suffix: sensors]]' with n: 3
|
3 sensors |
max:n |
At most n characters, the last of them replaced by …. Anything that is not a positive number leaves the value alone |
'[[name|max:10]]' with name: Kitchen temperature
|
Kitchen t… |
map:a=x,b=y |
Looks the value up in the pairs. *=z catches anything not named, wherever it is written. Matching is exact and case-sensitive. No match and no *: the placeholder is left |
'[[level|map:low=green,high=red,*=grey]]' with level: high
|
red |
find:text (v2.0.0+)
|
Where text first appears, counting from 0, or -1. A real number |
'[[id|find:car]]' with id: camera_1_car
|
9 |
In map:, each pair splits at its first =, so a result may contain = but a value to
match may not, and neither may contain a comma. =none matches an empty value:
map:=none,*=some.
An argument can hold a placeholder, which is filled in first (v2.5.0+):
[[room|lower|suffix:_[[kind]]]] with room: Hallway, kind: motion gives
hallway_motion. Before v2.5.0 the argument stopped at the inner ].
More: Transforms that take an argument,
find:.
(v2.0.0+)
Text is read as a list by splitting it at commas, trimming each item and dropping empty
ones. A variable that already holds a list is used as it is. These steps go first in a
chain, or straight after split.
| Step | What it does | Example | Result |
|---|---|---|---|
join / join:sep
|
The items as one line of text, , between them, or sep. join: puts nothing between |
'[[rooms|join: / ]]' with rooms: [Kitchen, Hall]
|
Kitchen / Hall |
split / split:sep
|
Text made into a real list, at commas or at sep. Text steps after it shape each item |
'[[ids|split]]' with ids: 'light.a, light.b'
|
[light.a, light.b] |
first |
The first item, as text |
'[[rooms|first]]' with rooms: [Kitchen, Hall]
|
Kitchen |
last |
The last item, as text |
'[[rooms|last]]' with rooms: [Kitchen, Hall]
|
Hall |
count |
How many items, or how many keys in a mapping. Unset, null or empty counts as 0. A real number |
'[[rooms|count]]' with rooms: [Kitchen, Hall]
|
2 |
After split, steps run on every item: [[rooms|split|slug|join:,]] with
rooms: 'Back Garden, Front Door' gives back_garden,front_door. first and last give
text even for a list of numbers: [[l|first]] on [1, 2] is "1".
More: Lists.
(v2.0.0+)
The value has to be a number, or text that is one ('3' works). Anything unusable, and
dividing by zero, leaves the value exactly as it was, with a warning in the console.
Results are real numbers, except pad, which is always text.
| Step | What it does | Example | Result |
|---|---|---|---|
add:n |
Adds n
|
'[[index|add:1]]' with index: 3
|
4 |
sub:n |
Subtracts n
|
'[[index|sub:1]]' with index: 3
|
2 |
mul:n |
Multiplies by n
|
'[[t|mul:2]]' with t: 21.5
|
43 |
div:n |
Divides by n. div:0 leaves the value alone |
'[[w|div:1000]]' with w: 2500
|
2.5 |
mod:n |
The remainder after dividing by n, with the sign of the value |
'[[index|mod:2]]' with index: 5
|
1 |
round / round:n
|
To a whole number, or to n places (0 to 15) |
'[[t|round:1]]' with t: 21.456
|
21.5 |
pad:n |
A whole number written with leading zeros to n characters (1 to 64), sign included. Always text |
'[[h|pad:2]]' with h: 7
|
"07" |
The number after the colon can itself be a placeholder: [[index|add:[[offset]]]]. Sums
chain: [[index|sub:1|mul:12|add:4]] with index: 3 is 28, and
[[t|mul:9|div:5|add:32|round:1]] turns 21 into 69.8.
round:2 gives 2.50 inside text ([[t|round:2]] kWh' is 2.50 kWh) but 2.5 as a whole
value, because a real number has no trailing zeros. Add text round it, or pad, when the
zeros matter.
More: Sums.
A value counts as missing when it is not set, is null, or is empty text. 0 and false
are values. With ?, an empty mapping {} is missing too (v2.6.0+), so [[size?]] with size: {} drops the key; without ? it is still a value.
| Step | What it does | Example | Result |
|---|---|---|---|
default:text (v1.2.0+)
|
text when the value is missing, or when a step before it found nothing. May hold spaces and colons. Empty (default:) gives empty text |
'[[name|default:Unnamed]]' with nothing set |
Unnamed |
or:other (v1.2.0+)
|
Another variable's value when this one is missing. Chain several |
'[[name|or:label|default:Unnamed]]' with label: Hall
|
Hall |
[[name?]] (v1.1.0+)
|
The key is removed when the value is missing (see the list below) |
icon: '[[icon?]]' with nothing set |
no icon key |
json (v1.2.0+)
|
The value written as JSON text: a mapping or list kept whole, a string quoted. Only as the first step |
'[[tap|json]]' with tap: {action: toggle}
|
{"action":"toggle"} |
What ? removes (v1.1.0+; emptied blocks v1.12.0+):
- a key whose whole value is the placeholder;
- a list item that is only the placeholder;
- any mapping or list left empty by those removals, all the way up; a block you wrote empty
yourself (
[],{}) is kept; - inside longer text, just the placeholder:
'x/[[n?]]'becomesx/.
Things worth knowing:
- Steps after
default:shape the stand-in as well:[[name|default:back garden|title]]givesBack Garden. Steps before it only shape a real value. -
default:can hold a placeholder:[[name|default:[[room]]]]. -
default:andor:make the result text.[[n|default:0]]withn: 5gives"5", not5. Leave them off a value that must stay a number or a boolean, and give the default in the template'sdefault:list instead. -
[[name|default:?]]drops the key when nothing is set (v1.13.0+). -
or:can borrow a mapping or a list (v2.6.0+) when the placeholder is the whole value:block: '[[size|or:default_size]]'puts the mapping in whole. Inside longer text it still cannot, and the gap stays. Before v2.6.0 it never could. - A mapping or list that is set passes through
default:andor:untouched (v2.4.0+), the same as[[name]]on its own.
More: Standing in for what nothing set, A variable the template can do without, Putting a mapping in on purpose.
| Step | What it does | Example | Result |
|---|---|---|---|
bool (v1.12.0+)
|
A real true or false. No: not set, null, false, 0, an empty list or mapping, and the words no, off, false, 0 or empty text, in any case. Yes: anything else |
hide_state: '[[compact|bool]]' with compact: 'yes'
|
true |
if:other (v1.12.0+)
|
Keeps the value only while other is a yes (read as bool reads it); otherwise it counts as missing |
'[[colour|if:alarm]]' with colour: red, alarm: false
|
missing, so left as written |
if:other=text (v1.12.0+)
|
Keeps the value only while other is exactly text
|
'[[colour|if:mode=night|default:amber]]' with mode: day
|
amber |
if:!other (v2.0.0+)
|
Keeps the value only while other is a no, or not set |
'[[note|if:!compact?]]' with compact: true
|
the key is gone |
if:other!=text (v2.0.0+)
|
Keeps the value only while other is anything but text, not set included |
'[[colour|if:mode!=off]]' with colour: teal, mode: auto
|
teal |
exists (v2.0.0+)
|
true when Home Assistant knows this entity id, as a state or in the entity registry. Not set or empty is false
|
'[[sensor|exists]]' with sensor: binary_sensor.hall_motion
|
true |
if_exists (v2.5.0+)
|
Keeps the entity id only while Home Assistant knows it, as exists reads it; otherwise it counts as missing |
- '[[sensor|if_exists?]]' with sensor: binary_sensor.attic_motion, which does not exist |
the list item is gone |
contains:text (v2.0.0+)
|
On text, true when text is in it. On a list, true when an item is exactly text. On a mapping, true when it has that key (v2.4.0+). Not set is false
|
'[[name|contains:door]]' with name: Outdoor Lights
|
true |
An if: that does not hold, or an if_exists that finds nothing, makes the value missing,
so pair it with ? to drop the key, or default: to use something else. Every if: in a chain has to hold, wherever it is
written. The variable if: reads may be dotted (if:room.on); the text after = is
compared as text, so if:count=3 matches the number 3.
bool, exists and contains: put in a real true or false when they are the whole
value, and the word true or false inside longer text. Further steps can turn the answer
into anything: [[compact|bool|map:true=Compact,false=Full]].
More: Switching an option on and off,
Keeping a value only while another variable says so,
exists, contains: and find:.
These read the value as an entity id and answer from Home Assistant. When there is nothing
to give (no such entity, no area, no device) the placeholder is left as written, and a
default: or or: after it stands in. The answer is always text, and Home Assistant's
words are never read as placeholders or as another card's code.
An entity's area is its own, or its device's when it has none.
| Step | What it gives | Example, for light.kitchen_ceiling
|
Result |
|---|---|---|---|
friendly_name (v1.1.0+)
|
The entity's name | '[[entity|friendly_name]]' |
Kitchen Ceiling |
area (v1.1.0+)
|
The name of its area | '[[entity|area]]' |
Kitchen |
device (v1.1.0+)
|
The name of its device | '[[entity|device]]' |
Ceiling Light |
attr:name (v1.1.0+)
|
One of its attributes. A list attribute comes out as a, b, a mapping as its JSON (v2.4.0+). access_token and entity_picture are never given out, nor anything carrying a token=
|
'[[entity|attr:brightness]]' |
"200" |
floor (v1.2.0+)
|
The name of its area's floor | '[[entity|floor]]' |
Ground Floor |
area_id (v1.2.0+)
|
Its area's id | '[[entity|area_id]]' |
kitchen |
device_id (v1.2.0+)
|
Its device's id | '[[entity|device_id]]' |
3f9c… |
domain (v1.7.0+)
|
The part before the dot. Needs no Home Assistant, only something shaped like an entity id | '[[entity|domain]]' |
light |
object_id (v1.7.0+)
|
The part after the dot, likewise | '[[entity|object_id]]' |
kitchen_ceiling |
labels (v1.7.0+)
|
The names of the entity's own labels, , between them. A device's labels are not included |
'[[entity|labels]]' |
Lights, Downstairs |
icon (v2.0.0+)
|
Its icon, set in the registry or by the integration; empty text when it has none | '[[entity|icon]]' |
mdi:ceiling-light |
device_class (v2.0.0+)
|
Its device class |
'[[sensor|device_class]]' on sensor.kitchen_temperature
|
temperature |
manufacturer (v2.0.0+)
|
Its device's manufacturer | '[[entity|manufacturer]]' |
IKEA |
model (v2.0.0+)
|
Its device's model | '[[entity|model]]' |
TRADFRI bulb |
area_icon (v2.0.0+)
|
Its area's icon; empty text when the area has none | '[[entity|area_icon]]' |
mdi:stove |
floor_icon (v2.0.0+)
|
Its floor's icon; empty text when the floor has none | '[[entity|floor_icon]]' |
mdi:home-floor-0 |
translate (v2.0.0+)
|
Home Assistant's own word in the dashboard's language: an integration's name for a word without a dot, a translation key for one with. Never left: anything untranslated comes back as written | '[[entity|domain|translate]]' |
Light |
state (v2.0.0+)
|
Its state, only for an entity listed in the card's rebuild_on; anything else is left, and default: does not cover that |
'[[mode|state]]' with rebuild_on: input_select.mode
|
Away |
domain, object_id and the steps after them chain like any others:
[[entity|object_id|replace:_: |title]] gives Kitchen Ceiling without asking Home
Assistant for a name. An entity listed in rebuild_on can be named directly:
[[input_select.mode|state]].
translate on a whole entity id treats it as a translation key and gives it back
unchanged, so it goes after domain: [[entity|domain|translate]].
More: Asking Home Assistant for a value, Following an entity's state.
(v2.3.0+)
These two build whole blocks rather than text, and each has to be the only step in its
placeholder, apart from |flat after a repeat: (v2.4.0+). The block is an item template's item:, or a variable that holds a mapping.
| Step | What it does | Example | Result |
|---|---|---|---|
with:k=v,k2=v2 |
The block once, filled in with those values. Values are always text |
- '[[row|with:entity=sun.sun,name=Sun]]' with row: {entity: '[[entity]]', name: '[[name]]'}
|
- {entity: sun.sun, name: Sun} |
with:var |
The same, with the values from a variable holding a mapping |
- '[[camera_entry|with:front]]' with front: {camera_entity: camera.front}
|
- {camera_entity: camera.front, live_provider: go2rtc}, the rest from camera_entry's defaults |
repeat:var |
The block once per item of a list, or once per thing a registry source finds, spliced into the list it sits in |
- '[[row|repeat:lights]]' with lights: [{entity: light.a}, {entity: light.b}]
|
- {entity: light.a, …} and - {entity: light.b, …}
|
with (v2.5.0+)
|
The same as with: with nothing after the colon: the block filled in from the values the card already has, item template defaults included |
- '[[row|with]]' with entity: light.kitchen on the card |
- {entity: light.kitchen, …} |
repeat (v2.5.0+)
|
An item template repeated over its own for_each list or for_each_from source |
- '[[every_light|repeat]]' with every_light: {for_each_from: {domain: light}, item: …}
|
one entry per light |
repeat:var|flat (v2.4.0+)
|
The same, with each copy that is itself a list laid out in the outer list, so a repeat inside a repeat gives one flat list | - '[[per_type|repeat:detect_list|flat]]' |
one entry per row of every copy |
A variable holding a fill, p: '[[probe|with:n=front_door]]', also offers the item
template's own values by dotted name (v2.4.0+): [[p.found]], if:p.found. See
Reading what a fill worked out.
Each repeat: copy gets the item's keys (or item for a plain value, and entity as well
when that value is an entity id), plus index, index0, count, first and last.
Copies that come out empty are dropped (v2.3.2+).
Inside the fill, an item template's own let: wins, then the with: values, then
everything the card already had, then the item template's default:.
More: List Entries.
A placeholder that is the whole value keeps its type; one inside longer text is always text.
| The value is | As the whole value | Inside text |
|---|---|---|
| text | text, even when it looks like a number ('12' stays "12") |
as written |
| a number | a number | its digits |
true / false
|
a boolean | the word |
| a mapping or a list | the structure itself | its JSON |
null |
null |
the word null
|
| empty text | empty text | nothing |
Once there are steps, the result is text, except when the last step that does something is one of these:
| Ends in | Gives |
|---|---|
bool, exists, contains:
|
a real true or false
|
add sub mul div mod round count find:
|
a real number |
split, and text steps after it |
a real list |
with:, repeat:
|
the filled block |
only if: steps |
the value untouched, mapping or list included |
default:, or: and if: are not counted as "the last step" after a sum, count, find:
or split: [[t|round|default:0]] is still a number when t is set. After bool,
exists or contains:, though, a default: or or: turns the answer into the text
"true" or "false". Leave them off when an option needs a real boolean; bool already
answers for an unset value. pad, first, last, json and everything that asks Home
Assistant give text. So does a value passed through default: or or: alone.
More: Value types.
When more than one place gives the same name a value, the first in this list wins.
| Where | Set by | Page | |
|---|---|---|---|
| 1 | The template's let: (v1.7.0+)
|
the template | Variables |
| 2 | The section's decluttering_let (v2.0.0+)
|
the dashboard | Variables |
| 3 | The view's decluttering_let (v2.0.0+)
|
the dashboard | as above |
| 4 | The dashboard's decluttering_let (v2.0.0+)
|
the dashboard | as above |
| 5 |
variables: on the card (for a repeated card: the item's values, then the card's variables:, then index and friends) |
the card | Using Templates |
| 6 | The section's decluttering_defaults (v1.10.0+)
|
the dashboard | Sharing Between Dashboards |
| 7 | The view's decluttering_defaults (v1.9.0+)
|
the dashboard | as above |
| 8 |
default inside a declaration in the template's variables:, or in a shared decluttering_variables declaration of the same name (v1.13.0+)
|
the template | Describing Variables |
| 9 | The template's default: list |
the template | Variables |
| 10 | The template's defaults: list (v2.0.0+)
|
the template | as above |
| 11 | The dashboard's decluttering_defaults (v1.1.0+); for a borrowed template, this dashboard's, then the lender's |
the dashboard | Sharing Between Dashboards |
| 12 | An entity listed in the card's rebuild_on, standing for itself (v2.0.0+)
|
the card | Variables |
| 13 | An item template, standing for its block (v2.3.0+) | the template | List Entries |
Within one list, the first entry for a name wins. A shared decluttering_variables
declaration only applies to a name the template declares itself.
In short: let is worked out and cannot be overridden; variables: on the card beats every
default; and the nearer a default is to the card (section, then view, then template, then
dashboard), the more it counts.
| Limit | What happens |
|---|---|
| 10 passes | Substitution repeats until nothing changes, at most 10 times. A variable that refers to itself stops there, with a console warning. A chain of variables that refer to each other settles in one pass when they are listed in the order they are used, one link per pass when listed backwards |
| A template that uses itself | Refused outright (v1.2.0+). See Troubleshooting |
| 3 keys down |
[[a.b.c.d]] is as deep as a dotted name reaches. List items cannot be picked by number |
| 6 deep |
with: and repeat: blocks inside each other |
| 2000 fills |
with: and repeat: copies in one card |
- Variables: how to use all of this, with longer examples.
- Configuration Reference: every key of templates, cards and the dashboard.
-
Repeat Reference: every key of
for_each_from, and what each copy is given. - A to Z: every key and step in one list.
Getting started
Core concepts
Content types
Features
- Repeating a Template
- Repeating Sections and Badges
- List Entries
- Sharing Between Dashboards
- Sharing a Template
- Visibility
- Styling
- Visual Editors
- Translations
Reference