Filters and data refresh

Your page can filter the data a published map shows - to one customer, one region, the last 30 days. It does so through declared filters: a fixed set of named, typed inputs that the map's author chose at publish time.

Declared filters exist on published maps. An organizational embed reads data as the signed-in viewer, so Fabric's own permissions and row-level security do the filtering there; use slicers for anything more.

Why filters are declared

A filter is the one place where a value from your page reaches the map's data. So the rule is strict, and it is the same for snapshot and live maps:

  • You can only name a filter the author declared. Anything else is rejected before it is applied.
  • Values are checked against the declared type, and on live maps are bound as query parameters on the server - never pasted into a query.
  • A filter narrows what the map shows. It can never reach data the publish does not already include.

This is what makes it safe to take filter values from a URL or a user's session. It is not a security boundary between users: anyone who can open the map can change the filter. To restrict what a particular user may see, use a token-gated map with row-level security, or an organizational embed.

Discover the filters

const schema = await map.getFilterSchema();
[
  { name: "region",   type: "stringList", operators: ["in"],                 label: "Region", column: "Region", values: ["EMEA", "AMER", "APAC"] },
  { name: "revenue",  type: "number",     operators: ["in", "between"],      label: "Revenue" },
  { name: "period",   type: "dateRange",  operators: ["between", "relative"], label: "Order date" }
]

Build your filter UI from this rather than hard-coding names: it then keeps working when the author adds a filter, and it tells you which operators each one accepts.

Authors: when a map is published, Icon Map declares a filter automatically for every column one of its slicers can reach. So to give a hosting page a filter on a column, add a slicer on that column and publish again. Your page can then hide that slicer from readers - see Lock or hide the matching slicer.

Set filters

await map.setFilters({ region: ["EMEA", "APAC"] });          // replaces all your filter values
await map.clearFilters();
const current = await map.getFilters();

Set them at mount to avoid loading unfiltered data first:

embed(el, { publishId, filters: { region: ["EMEA"] } });

Value shapes

Filter type Value Meaning
string, number, boolean, date "EMEA" equals
stringList, numberList ["EMEA", "APAC"] any of
number, numberList { low: 1000, high: 5000 } between (either end optional)
date, dateRange { from: "2026-01-01", to: "2026-03-31" } between, ISO days (either end optional)
dateRange { operator: "inLast", count: 30, unit: "days" } relative to today

Relative dates take operator "inLast", "inThis" or "inNext"; unit "days", "weeks", "months" or "years"; and an optional includeToday. The window is worked out against today's date each time the filter is applied.

await map.setFilters({ period: { operator: "inLast", count: 30, unit: "days", includeToday: true } });

Change one filter at a time

updateFilters changes some filters without restating the rest - the same model as Power BI's updateFilters.

await map.updateFilters({ operation: "add",        filters: { region: ["EMEA"] } });   // merge into what is set
await map.updateFilters({ operation: "replace",    filters: { region: ["APAC"] } });   // overwrite these names only
await map.updateFilters({ operation: "replaceAll", filters: { region: ["AMER"] } });   // same as setFilters
await map.updateFilters({ operation: "removeAll" });

To remove a single filter, set the values you want to keep:

const { region, ...rest } = await map.getFilters();
await map.setFilters(rest);

Lock or hide the matching slicer

When your page sets a filter, the reader may still see a slicer in the map for the same column and try to change it. display deals with that:

await map.updateFilters({
  operation: "replace",
  filters: { region: ["EMEA"] },
  display: { region: { locked: true } },
});

locked and hidden both remove the slicer bound to that filter's column from the reader's view, so what your page pinned stays pinned. displayName renames it.

This is presentation, not protection. A reader who opens the map's address directly still sees every region the publish contains.

Errors

Code When
undeclared_filter A name the author did not declare. detail.undeclared lists them. Nothing was applied.
invalid_args A value of the wrong shape for the filter's type.
denied_capability The author switched the Filters group off.
unsupported_command This embed has no declared filters (an organizational embed).

filtersChanged fires after every change, with a reason.

Refresh live data

On a map published with live data, refreshData re-runs the queries past the cache:

const { refreshed } = await map.refreshData();                       // every live dataset
await map.refreshData({ bindingIds: ["orders"] });                   // just one

map.on("dataStateChanged", ({ state, bindingIds }) => showSpinner(state === "loading"));

On a snapshot map it resolves with an empty list - there is nothing to re-run until the author publishes again. capabilities.map.dataMode tells you which you have: "live", "snapshot" or "mixed".

Every refresh spends the map owner's Fabric capacity, so it is rate-limited and counts against the publish's query budget. Expect rate_limited or budget_exceeded if your page refreshes on a tight timer; the author can also switch the Data refresh group off, which the server enforces as well as the map.