Skip to content

Variables

tempus2016 edited this page Aug 20, 2026 · 6 revisions

Variables

Variables are the holes in a template. You write [[name]] in the template, and pass a value for name when you use it.

decluttering_templates:
  room_light:
    card:
      type: tile
      entity: '[[light]]'
      name: '[[room]]'
type: custom:decluttering-card-plus
template: room_light
variables:
  - light: light.kitchen
  - room: Kitchen

Writing placeholders

A placeholder is a name in double square brackets: [[light]], [[room]], [[accent]].

When a placeholder is the whole value, quote it.

entity: '[[light]]'     #
entity: [[light]]       # ❌ not valid YAML - "[" starts a list

Inside a longer string, quotes are optional but harmless.

name: [[room]] light'
title: Lights in [[room]]

Placeholders work anywhere in the content — keys' values, nested mappings, list items, even inside CSS in a style block. They are substituted throughout the whole template body.

Passing values

variables: on the card can be written either way. A list, one name per entry:

variables:
  - light: light.kitchen
  - room: Kitchen

or a single mapping:

variables:
  light: light.kitchen
  room: Kitchen

Both are read the same way, here and in a template's default:. The list form is what the visual editor writes, so a card edited there comes out as a list whichever way you typed it.


Default values

default: on the template supplies fallbacks, so callers only pass what differs.

decluttering_templates:
  area_sensor:
    card:
      type: tile
      entity: '[[entity]]'
      name: '[[label]]'
      icon: '[[icon]]'
    default:
      - label: [[area]] temperature'
      - area: Living Room
      - entity: sensor.living_room_temperature
      - icon: mdi:thermometer

Used with no variables at all, it renders entirely from the defaults. Override one, and only that one changes:

# 1. nothing passed - pure defaults
- type: custom:decluttering-card-plus
  template: area_sensor

# 2. override the area, and the label follows
- type: custom:decluttering-card-plus
  template: area_sensor
  variables:
    - area: Bedroom
    - entity: sensor.bedroom_temperature

# 3. override the label outright
- type: custom:decluttering-card-plus
  template: area_sensor
  variables:
    - label: Solar output right now
    - entity: sensor.solar_power
    - icon: mdi:solar-power

Three cards from one template showing defaults, partial override and full override

default: uses the same shape as variables: — a list, or a single mapping.

The default values are also what the visual editor uses to draw its preview, so giving every variable a sensible default makes templates much nicer to work on.

A template can go further and describe each variable — a label, a helper line and a Home Assistant selector — so that cards using it get a real entity picker or dropdown instead of a YAML box, and so the editors can warn about variables that are missing or unused. See Describing Variables.

Defaults that reference other variables

A default can contain placeholders of its own. Above, label defaults to [[area]] temperature', so changing area changes the label without the caller ever mentioning label.

Substitution repeats until nothing is left to replace, so the order you declare them in does not matter, and a value you pass always beats a default — including when the placeholder only appears because of an earlier substitution.

This also gives you "fall back to another variable":

default:
  - title: '[[name]]'      # if no title is given, use the name
  - name: Sensor

Loops

A variable that refers to itself can never be resolved. Substitution gives up after 10 passes and logs a warning to the browser console rather than hanging:

decluttering-card-plus: gave up substituting variables after 10 passes.
Check whether a variable refers to itself.

Ten passes is far more than any sane template needs — chains that deep are fine, cycles are not.


Asking for a variable in a different shape

A placeholder can ask for its value written differently, which lets one variable serve both a room's name and the entity id built from it:

decluttering_templates:
  room_tile:
    card:
      type: tile
      entity: 'light.[[room|slug]]'
      name: '[[room|title]]'
    default:
      - room: Back Garden

light.[[room|slug]] becomes light.back_garden, and [[room|title]] becomes Back Garden. Pass room: John's Shed and you get light.john_s_shed without the caller ever mentioning the entity id.

Transform Does John's Back Garden becomes
slug Lower case; anything that is not a letter or digit becomes _ john_s_back_garden
upper Upper case JOHN'S BACK GARDEN
lower Lower case john's back garden
title Capitalises each word John's Back Garden
kebab Lower case; anything that is not a letter or digit becomes - john-s-back-garden

Transforms chain, left to right: [[room|slug|upper]] gives JOHN_S_BACK_GARDEN. Order matters, since slug lower-cases first — [[room|upper|slug]] gives john_s_back_garden.

The same variable can be used raw and transformed in the same template.

A transform is a way of writing a value out, not a way of choosing it, so a transformed placeholder is always text. That makes it a tool for scalars: a number transforms as its digits, but a mapping or a list under a transform is left unsubstituted — slugging or uppercasing its JSON would only garble it, so the placeholder stays visible instead.

Only those five are transforms. A word after the bar that is not one of them is not recognised, so nothing is substituted and you are left looking at [[room|shout]] in the card. That is deliberate: a silent empty string would be far harder to spot. The same goes for a chain: one unknown word in it leaves the whole placeholder alone rather than applying half of it.


Asking Home Assistant for a value (v1.1.0+)

A placeholder can also ask Home Assistant about the entity it names, which is what lets a template default a name to the entity's own name rather than making every card pass one:

decluttering_templates:
  room_tile:
    card:
      type: tile
      entity: '[[entity]]'
      name: '[[entity|friendly_name]]'
Asks for Gives Read from
friendly_name What the entity is called Its friendly_name, then the name you gave it, then its original name
area The name of the area it is in Its own area, or its device's
device The name of its device The name you gave the device, then the device's own
attr:<name> One named attribute The entity's current attributes

These chain with the transforms above and run in the order written, so [[entity|friendly_name|slug]] is the entity's name slugged. The value a resolver reads is an entity id, so it has to come first: [[entity|slug|friendly_name]] would slug the id and then go looking for an entity by that name.

The entity's state is deliberately not here. A card's configuration is built once, so resolving state would mean rebuilding the whole card every time anything changed. What is here comes from the registry, which changes about as often as the dashboard does — and a template that uses it is rebuilt when it does, so a name that was not known yet when the page first painted still turns up.

When Home Assistant has nothing to give — an entity that does not exist, an attribute it does not carry, no area on it — the placeholder is left in the card and the browser console says which and why.


A variable the template can do without (v1.1.0+)

A placeholder written [[name?]] is one the card can manage without. When nothing gives it a value, the option it stands for is taken out of the card altogether rather than left showing the brackets:

decluttering_templates:
  room_tile:
    card:
      type: tile
      entity: '[[entity]]'
      name: '[[name?]]'

Pass a name and the tile uses it. Leave it out and the tile has no name key at all, so Home Assistant falls back to the entity's own name — usually what you wanted.

Empty means unset, null, or the empty string. A 0 and a false are values and stay. Inside a longer piece of text it simply leaves quietly, and an item of a list that is nothing but an empty option is dropped rather than leaving a hole. It is not a mistake, so nothing is said about it in the console.

The marker goes at the end, after any transform: [[room|slug?]].


Writing [[ and meaning it (v1.1.0+)

A placeholder written [[!name]] is not a variable: it renders as the literal text [[name]]. That is the only way to put those brackets in a template — worth knowing if yours holds markdown, or Jinja that uses them.

card:
  type: markdown
  content: 'Write [[!entity]] to use a variable.'

The bang is dropped only once every other substitution is done, so an escaped placeholder can never be turned back into a real one. The editors know about it too, and will not tell you that [[!entity]] is a variable you forgot to set.


Value types

How a value is substituted depends on where the placeholder sits.

The placeholder is the entire value

The type is preserved. Numbers stay numbers, booleans stay booleans, and mappings and lists are inserted as structure.

decluttering_templates:
  sized_tile:
    card:
      type: tile
      entity: '[[entity]]'
      name: '[[name]]'
      grid_options:
        columns: '[[cols]]'      # whole value
        rows: 1
variables:
  - cols: 12                     # inserted as the number 12, not "12"

One template rendered at six, four and three column widths

This matters: columns: "12" would be a string and Home Assistant would ignore it.

The same applies to structures:

card:
  type: tile
  entity: '[[entity]]'
  features: '[[features]]'
variables:
  - features:
      - type: toggle
      - type: light-brightness

and to booleans and null:

variables:
  - show_name: true
  - icon: null

The placeholder is part of a longer string

The value is inserted as text.

name: [[room]] light'

With room: Kitchen this becomes Kitchen light. A mapping or list used this way is inserted as its JSON text, which is occasionally useful and usually not what you want.

Awkward characters are handled

Values containing quotes, backslashes, newlines or tabs are escaped correctly. So multi-line values and Jinja templates work:

variables:
  - message: >-
      Line one
      Line two
  - jinja: "{{ states('sensor.x') | float * 2 }}"

Values containing $& or $1 are inserted literally rather than being interpreted as replacement patterns, and variable names containing regex characters are matched literally too.


Unresolved placeholders

If a placeholder has no matching variable and no default, it is left in place. You will see the literal text [[light]] in the rendered card, or Home Assistant complaining about an unknown entity called [[light]].

That is the number one symptom of a typo in a variable name. See Troubleshooting.

The card says so in the browser console (v1.1.0+), naming the template and every variable still standing:

decluttering-card-plus: template "room_tile" uses [[light]], which nothing gives a value
to, so it is rendered as written. Set it on the card, or give it a default in the
template. To write those brackets on purpose, escape it as [[!light]].

An escaped [[!light]] and an optional [[light?]] are not mistakes, so neither is reported.

You will also see this in the visual editor while editing a template — the card editor there is looking at the raw template, so it reports [[light]] as an unknown entity. That is expected and harmless.


Reference

Where Example Result
Whole value, string entity: '[[e]]' with e: light.x light.x
Whole value, number columns: '[[c]]' with c: 12 12 (number)
Whole value, boolean show: '[[s]]' with s: true true (boolean)
Whole value, mapping/list features: '[[f]]' inserted as structure
Whole value, null icon: '[[i]]' with i: null null
Inside a string name: [[r]] light' with r: Hall Hall light
No such variable entity: '[[nope]]' left as [[nope]]
Transform Example Result
slug `'light.[[r slug]]'withr: Back Garden`
upper / lower `'[[r upper]]'withr: Hall`
title `'[[r title]]'withr: back garden`
kebab `'[[r kebab]]'withr: Back Garden`
Chained `'[[r slug
Not a transform `'[[r shout]]'`
Asking Home Assistant (v1.1.0+) Example Result
friendly_name `'[[e friendly_name]]'`
area / device `'[[e area]]'`
attr:<name> `'[[e attr:brightness]]'`
Nothing to give `'[[e area]]'` with no area
Optional and escaped (v1.1.0+) Example Result
Optional, unset name: '[[n?]]' the name key is removed
Optional, set name: '[[n?]]' with n: Hall Hall
Escaped content: '[[!n]]' the text [[n]]
Shape Read as
- light: light.x per line one variable per entry
light: light.x in a mapping one variable per key
An entry with several keys one variable per key
Precedence Winner
variables on the instance vs any default variables
default inside a declaration vs the default: list the declaration
Same key twice in one list the first one

Next

Describing Variables to give them labels, pickers and warnings → Repeating a Template to render one template once per item → Cards · Badges · Rows · ElementsRecipes for finished examples

Clone this wiki locally