Home Assistant Map Card

August 19, 2026 · View on GitHub

Take a look at the blog post introducing the custom:map-card for Home Assistant.

An example of the custom:map-card

Installation

HACS

Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.

Manual

  1. Copy map-card.js to your /var/lib/hass/www folder.
  2. Click on Edit Dashboard, Manage resources add /local/map-card.js as JavaScript Module.

Usage

TIP

Home Assistant contains a native map feature, if you don't need advanced features like WMS layers it might be a better choice.

Minimal

type: custom:map-card
x: 51.23
y: 3.652

More advanced

TIP

Take a look at: https://nathan.gs/2024/01/06/ha-map-card-a-new-and-alternative-leaflet-based-map/#a-more-advanced-example-measuring-pm25-air-quality-for-my-home

Options

nameDefaultnote
xLongitude
yLatitude
history_startDate: 2022-03-01T12:00:00Z
Time code: 5 hours ago
Entity: input_number.example_number
See History options for full details.
history_endnowDate: 2022-03-01T12:00:00Z
Time code: 5 hours ago
Entity: input_number.example_number
See History options for full details.
focus_entityEntity to focus on. Map will default to show all markers if this and X/Y are not set.
titleIf empty, don't show a title
zoom12The zoom level. Ignored unless focus_entity or X & Y coords are set.
card_size5The Home Assistant card size
entities[]Array of entities, will be listed as markers
wms[]WMS Layers, an array of WMS see below
tile_layers[]Tile Layers, an array of Tile Layers see below
tile_layer_urlhttps://tile.openstreetmap.org/{z}/{x}/{y}.png or https://tile.example.com/{{states('sensor.test') }}/{z}/{x}/{y}.pngOverride the default map source, the url supports entity lookups. See URL Entity lookup.
tile_layer_attribution&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>Set the correct map attribution
tile_layer_options{}The options for the default TileLayer
history_date_selectionfalseWill link with a energy-date-selection on the page to provide an interactive date range picker.
theme_modeautoauto, light ordark
focus_follownonenone, refocus, contains, reset the map focused entity's, on each update. Some people call this the Autofit feature.
focus_follow_pause0Number of seconds to suspend focus_follow after the user pans (mouse down/drag) or zooms the map. 0 disables pausing (default, existing behavior).
map_options{}The options for the default Leaflet Map
cluster_markersfalseEnable marker clustering to group nearby entities together. Click the group icon button to toggle clustering on/off.
debugfalseEnable debug messages in console.
plugins[]An array of plugin definitions, see: Plugin Options, Available plugins and Developing plugins

For non-geographic maps, set map_options.crs to simple. The card will translate that into Leaflet's L.CRS.Simple, so you can use image/floor-plan style coordinates and pair them with entity fixed_x / fixed_y values.

type: custom:map-card
x: 0
y: 0
zoom: 0
map_options:
  crs: simple
  minZoom: -5
tile_layer_url: ""
entities:
  - entity: sensor.room_temperature
    fixed_x: 320
    fixed_y: 180

If x & y or focus_entity is not set it will take the lat/long from the first entity.

URL Entity lookup

You can add dynamic url's, for example to use a sensor value in the url. When the parameter changes, the map will redraw with the new tile layer.

tile_layer_url: https://tile.example.com/{{states('sensor.test') }}/{z}/{x}/{y}.png
TIP

It only supports states without any filters, this is a crude javascript regex based implementation, not the Jinja2 templating engine from Home Assistant.

Entity options

Either the name of the entity or:

nameDefaultnote
entityThe entity id
displaymarkericon, state, attribute, marker or pill.
marker will display the picture if available.
icon will display the icon if available, otherwise a label composed of first letters of the entity's name.
pill shows the entity's current zone icon + its initials together when it is inside a Home Assistant zone, and the normal initials marker otherwise. See Place pill.
pictureSet a custom picture to use on the marker.
iconSet a custom icon to use if display is set to icon. e.g. mdi:cake
labelSet a custom text label to display on the marker (overrides auto-generated initials). On picture markers, label / prefix / suffix overlay the photo; auto-generated initials do not.
attributeSet an attribute to use if display is set to attribute. e.g. speed
prefixOptional prefix for a value if display is set to attribute
suffixOptional suffix for a value if display is set to attribute
size48Size of the icon
colorRandom ColorCan defined as red, rgb(255,0,0), rgba(255,0,0,0.1), #ff0000, var(--red-color)
csstext-align: center; font-size: 60%;CSS for the marker (only for state and marker)
history_startWill inherit from map config if not set.
Date: 2022-03-01T12:00:00Z
Time code: 5 hours ago
Entity: input_number.example_number
See History options for full details.
history_endnowWill inherit from map config if not set.
Date: 2022-03-01T12:00:00Z
Time code: 5 hours ago
Entity: input_number.example_number
See History options for full details.
history_line_colorRandom ColorCan defined as red, rgb(255,0,0), rgba(255,0,0,0.1), #ff0000, var(--red-color)
history_show_linestrueShow the path
history_show_dotstrueShow little dots on the path
fixed_xDisplay a fixed marker, this will ignore the latitude/longitude attributes
fixed_yDisplay a fixed marker, this will ignore the latitude/longitude attributes
fallback_xIf the latitude/longitude is missing, use these fixed attributes
fallback_yIf the latitude/longitude is missing, use these fixed attributes
tap_action{"action": "more-info"}Allow custom action to be triggered when this entity is clicked. Actions include more-info, call-service, navigate, url, assist, none. Some actions require additional paramaters. navigate requires a navigation_path. url requires a url_path. call-service requires service and an optional data
gradual_opacityThe total amount of opacity will be gradually distributed across the paths. gradual_opacity is divided by the number of paths to calculate the opacity step for each path. The base opacity is generated by (1 - gradual_opacity), and as each path progresses, its opacity gradually darkens by the step amount.
focus_on_fittrueIf this variable is set to false, This entity will be excluded when the map fits the included entities on the screen.
z_index_offset1z-index value that determines what is displayed on top when markers overlap. (Setting a gap of at least 20 between the values assigned to each entity is recommended.)
use_base_entity_onlyfalseWhen set to true, the tracking will use only the base entity without including any associated device trackers. This is useful for scenarios where you want to track the base entity directly and ignore any associated trackers.
position_update_threshold10Distance threshold in meters. Marker position only updates if the entity has moved more than this distance. Prevents unnecessary map updates from GPS drift. Useful for clustered markers.
pill_callout_min_zoom15Only for display: pill. Map zoom at/above which the pill is drawn as an offset callout (up-left of the point, with a thin leader line to a dot on the exact location). Below this zoom the pill simply centres on the point with no leader, so it isn't obtrusive when the whole region is in view.
circleDisplay a circle around the marker.
More details Circle options
geojsonDisplay GeoJSON data from an entity attribute.
More details GeoJSON options

Place pill (display: pill)

A pill marker answers "who is where" in a single glance. When a tracked entity is inside a Home Assistant zone, its marker is drawn as a small stadium ("pill") holding two circles: the zone's icon on the left and the entity's initials on the right — for example a church icon next to ML, or a home icon next to MG. When the entity is not in any zone it falls back to the normal initials marker. It is opt-in per entity and changes nothing for entities that don't set display: pill.

Why

With the stock marker you can see that someone is on the map, but to learn which named place they're at you have to recognise the spot or click through. And when a person sits on top of their home/zone, you either get two overlapping markers or a cluster bubble showing a count. The pill folds identity and place into one marker — "Mom is at the church" is readable without interaction — using data Home Assistant already has (zones + the entity's state).

How it resolves the person and the place

There is no extra configuration linking a person to a place; it is derived live:

  1. The entity is the person. You list a person.* or device_tracker.* on the map as usual. The card already places it at its GPS position and derives its initials from the entity's friendly_name (first letter of each word, e.g. Mom LocationML). label: still overrides the initials if set.
  2. Zones are the named places. Each HA zone.* has a friendly_name, an icon, a latitude/longitude and a radius. These already exist whenever you create a zone (Settings → Areas & Zones).
  3. Home Assistant links them via state. When a device_tracker/person is inside a zone, HA sets that entity's state to the zone's name (the Home zone reports home). This is the join between person and place, maintained by HA core — the card doesn't compute geometry.
  4. The card reads it back. For a pill entity it takes the entity's raw state, finds the zone.* whose friendly_name matches it (case-insensitive; home → the Home zone), and uses that zone's icon for the left circle. If the state is not_home/away/unknown — or matches no zone — there is no place, so the normal initials marker is shown instead.
  5. Tooltip. Hovering shows <person> is at <place>, e.g. Mom is at Extended Family. The person name is the entity's friendly_name with a trailing Location/Tracker/Device/Phone/GPS stripped, so Mom Location reads as Mom. (This uses a Leaflet tooltip, which opens immediately, rather than the browser's delayed native title.)

The marker is rebuilt automatically when the entity enters or leaves a zone, so the pill appears, swaps icon, or collapses to initials as the person moves.

Zoom-aware callout

At/above pill_callout_min_zoom (default 15) the pill is offset up-and-left of the exact position and connected by a thin leader line to a small dot on the real location, so the pill doesn't cover what's underneath. Below that zoom — when the whole town or region is in view and a leader line would just be clutter — the pill simply centres on the point with no leader. Set pill_callout_min_zoom lower to engage the callout sooner, or very high to effectively disable it.

Appearance

The pill reuses the entity's existing color (the same per-entity colour the normal marker uses, or whatever you set with color:) for the outline, icon, initials and leader, and size for each circle. Dark mode is honoured.

Requirements

  • The places must be real HA zones with an icon and a friendly_name (icons like mdi:home, mdi:church, mdi:account-group).
  • The entity must report its zone in its state — GPS device_trackers and persons do this automatically; the Home zone reports home.

Example

type: custom:map-card
entities:
  - entity: device_tracker.mom_location
    display: pill
  - entity: person.matt
    display: pill
    pill_callout_min_zoom: 14   # show the offset callout a bit sooner

With device_tracker.mom_location in the Extended Family zone this renders a pill of mdi:account-group + ML, tooltip "Mom is at Extended Family"; when she leaves it becomes a plain ML marker.

History options

If history_date_selection:true, any entities that do not define their own history_start and history_end configuration will be automatically linked to this. Please ensure a card of type: energy-date-selection exists on the page before enabling this.

This can be added via the "Add Card" dialog by selecting Manual and entering the text type: energy-date-selection.

Alternatively history_start and history_end can be set to

  • A specific date such as 2022-03-01T12:00:00Z
  • A time code such as 10 days ago 4 hours ago 1 week ago etc.
  • An entity that will provide either a date or number (which will be used as the amount of hours ago to show). e.g. input_number.example_number_value

If you want to specify your own unit, configure the history_start/history_end as the below.

history_start:
  entity: input_number.example_number_value
  suffix: days ago

Each entity can individually override the base config by setting its own history_start/history_end, using any of the options above. Any entity without its own settings will inherit the map level config.

Circle options

Display a circle around the marker. It can either be called with the string auto or following options.

circle: auto

nameDefaultnote
sourceWhere to get the radius from, see below for options.
attributeThe attribute to use for the radius (in case of source: attribute).
radiusRadius in meters (in case of source: config)(optional)
colorColor of the circle (will use the entity color if not set)
fill_opacity0.2Opacity of the fill color

Source

  • auto - Will use the gps_accuracy, then the radius attribute from the entity if available, otherwise will use the radius set in the config.
  • gps_accuracy - Will use the gps_accuracy attribute from the entity.
  • radius - Will use the radius attribute from the entity.
  • config - Will use the radius set in the config.
  • attribute - Will use the attribute set in the config.

GeoJSON options

Display GeoJSON data from an entity attribute. This is useful for displaying zones, areas, routes, or any geographic data stored as GeoJSON in your Home Assistant entities.

The geojson option can be configured in several ways:

Simple usage (uses default attribute name geo_location):

geojson: true

Specify a custom attribute name:

geojson: zone_geojson

Full configuration:

geojson:
  attribute: zone_geojson
  color: '#FF5733'
  weight: 3
  opacity: 1.0
  fill_opacity: 0.2
  hide_marker: false
nameDefaultnote
attributegeo_locationThe entity attribute containing the GeoJSON data
colorEntity colorColor for the GeoJSON features (lines and fills)
weight3Line weight for GeoJSON features
opacity1.0Opacity of lines
fill_opacity0.2Opacity of filled areas
hide_markerfalseWhen set to true, hides the default entity marker and only displays the GeoJSON

Supported GeoJSON types:

  • Point, MultiPoint
  • LineString, MultiLineString
  • Polygon, MultiPolygon
  • GeometryCollection
  • Feature, FeatureCollection

Example entity configuration:

entities:
  - entity: sensor.my_zone
    geojson:
      attribute: zone_data
      color: '#3388ff'
      fill_opacity: 0.3
      hide_marker: true

The GeoJSON data in the entity attribute can be either:

  • A JSON string: '{"type": "Polygon", "coordinates": [[[0,0], [1,0], [1,1], [0,1], [0,0]]]}'
  • A parsed JSON object (if your integration provides it that way)

Interactive Features:

  • GeoJSON zones are clickable - clicking on any GeoJSON feature will show the entity's more-info dialog (or trigger the configured tap_action)
  • If the GeoJSON Feature or FeatureCollection includes properties, they will be displayed in tooltips when hovering over the features

WMS and tile_layers options

namenote
urlThe url of the layer, see URL Entity lookup.
optionsThe leaflet layer WMS options or Tile Layer options
historyThe name of the layer option which controls the dat, if it supports a date or time option. Set history to the name of this property. The history_start value, state or date range picker will then set this property on the layer and update it as necessary.

Zooming & tile_layer_options.maxZoom

By default the map-card will not zoom beyond default property of the maxZoom of the tilelayer, the default is 18, but it can be overriden as follows:

type: custom:map-card
tile_layer_options:
  maxZoom: 20

Keep in mind that the tile layer source also has a maximum zoom level, which is 20 for most OSM maps.

OSM & tile_layer_options.referrerPolicy

OpenStreetMap tile servers may block tile requests that lack a valid Referer header. By default, referrerPolicy is set to "origin-when-cross-origin", which ensures the browser sends the origin with cross-origin tile requests as required by OSM's Tile Usage Policy. You can override this if needed:

type: custom:map-card
tile_layer_options:
  referrerPolicy: "no-referrer"

Advanced WMS/Tile layer options

More complex use of the WMS/Tile history property can be configured within the history property of the layer.

  • property is the option this should control (often named time or date)
  • source defaults to auto (which means it will inherit from the main map settings). Set this to a date or number entity if this is different.
  • suffix days ago/weeks ago as with other history entities
  • force_midnight some WMS/Tile layers only work if the date is set as midnight.
  history:
    property: time
    source: input_number.test_number_value
    suffix: months ago
    force_midnight: true

Plugin options

namenote
nameMandatory, a helpful name for this instance of the plugin. Useful for debugging issues.
urlThe url of the plugin, if the plugin is in the file /var/lib/hass/www/SomePlugin.js , then this would be ./local/SomePlugin.js.
hacs.moduleThe HACS module from which the plugin must be loaded if url is not set.
hacs.fileThe file within the module from which the plugin must be loaded if `url is not set.
optionsOptions for configuring the plugin

The url option is used when set. If the url is not set and the hacs object is, the plugin will be loaded from the HACS plugin with the given name and given filename.

Example config with plugin

type: custom:map-card
x: -25.3744
y: 133.7751
plugins:
  - name: plugin
    url: /local/my-plugin.js
    options:
      some_option: true
  - name: another_instance
    url: /local/my-plugin.js
    options:
      some_option: false

Available Plugins

namedescription
bom-radarDisplays the Australian BoM rainfall radar for the past 90 minutes and the radar forecast for the next 90 minutes as an overlay on the map.
buienradarDisplays buienradar.nl as an overlay on the map

You can find more plugins using the ha-map-card-plugin topic.

Extra Tile Layers

The leaflet-extras has a nice list of available tile layer providers.

Development

git clone git@github.com:nathan-gs/ha-map-card.git

This project uses devenv.sh.

  1. Install devenv
  2. devenv shell and then watch or devenv shell watch to immediatly drop into a watched shell.

NPM alternatives

  • npm install
  • npm run build (npm run watch to update on change)

Developing Plugins

Tag your Github repo with ha-map-card-plugin for discoverability.

Mentions & Discussions

Showcase

We have a gallery of nice examples at nathan.gs/ha-map-card, contributions are welcome, check the showcase/README.md.