Problem statement
A Fabric App runs embedded in the Fabric Portal, so it has no addressable URL of its own.
The address bar belongs to the portal, and cross-origin restrictions prevent the app from
reading or writing the parent frame's location. There is therefore no supported way to
pass state into an app at launch, or to reflect the app's current state in a link.
This removes deep linking entirely — a capability every non-embedded web application has,
and one that users of an analytical app expect.
Concretely, none of the following are possible today:
- Sharing a specific view with a colleague by sending a link
- Linking from Teams, email, a wiki or a SharePoint page into a particular state of an app
- Embedding a "reopen this analysis" link in an exported file
- Linking from a Power BI report into a deeper analysis in a Fabric App
- Bookmarking a state in the browser
- Preselecting filters or context when launching an app from elsewhere
We are building an analytical client for Power BI Semantic Models as a Fabric App. Sharing
a saved analysis with a colleague is one of its core workflows. Without deep linking, the
best we can offer is: the user copies a short identifier, sends it in a chat message, and
the recipient opens our app and pastes it into a dialog. That is a poor experience next to
sending a link, and it is the kind of friction users read as the platform being immature
rather than as a deliberate constraint.
Proposed solution
Extend the existing embedded-host postMessage bridge (@microsoft/fabric-embedded-host) with
a small state channel. The transport already exists, so this should be an addition to a
known protocol rather than new infrastructure.
Both directions are needed. Reading alone is not sufficient: without the ability to write,
nothing can produce a shareable link for the read side to consume.
-
READ — launch state
const state = await fabric.app.getLaunchState();
// => { view: "sales-by-region", filter: "AT", period: "2026-Q2" }
A plain key/value object supplied by the host, taken from the portal URL. Available
before first render so the app can restore without a visible flash of default state.
-
WRITE — reflect state in the portal URL
fabric.app.setState({ view: "sales-by-region", filter: "AT" }); // push
fabric.app.replaceState({ ... }); // replace
The host owns encoding, escaping and how the state appears in its URL. The app supplies
an opaque object; it never manipulates the URL directly.
-
CHANGE NOTIFICATION
fabric.app.onStateChange((state) => { ... });
So browser back/forward can work within an app rather than navigating the portal away
from it, which is the current behaviour and is easy for a user to trigger accidentally.
Security considerations, since they are the likely objection:
- Values are opaque to the host and returned to the app verbatim; the host performs no
interpretation, so there is no injection path into portal chrome.
- A documented size limit is fine — a few KB covers every case we have. Applications
needing more can store the payload themselves and put an identifier in the URL, which is
exactly what a size limit should push people toward.
- Scope the state to the app item, so one app cannot read another's.
- Read-only launch state alone would already be a large improvement if the write side
raises concerns; please consider shipping them separately rather than neither.
Alternatives considered
-
Identifier plus manual entry (what we are building now)
State is persisted in the app's own database and the user copies a short identifier that
the recipient pastes into an "Open by identifier" dialog. It works, but it replaces a
single click with copy, paste, and a dialog, and it fails the most common case entirely:
sending someone a link in a chat message.
-
Reading the parent frame's URL directly
Not possible cross-origin, and it would be the wrong approach even if it were — apps
should not be reading host chrome.
-
Encoding state in the app item's own configuration
Makes state global to the item rather than per link, so two users cannot hold different
states at once. Unusable for sharing.
-
A separate share service outside the app
Adds infrastructure to solve a problem the platform is better placed to solve once, and
every Fabric App with shareable state would have to build it independently.
Additional context
Precedent within the platform: Power BI report URLs already carry deep-link state such as
report bookmarks and filter parameters, and Fabric item URLs already carry item identifiers.
The concept exists in the portal; Fabric Apps simply have no access to it.
We are happy to test a preview implementation against a real workload — a data-heavy
analytical app where shared, reproducible state is central rather than incidental.
Problem statement
A Fabric App runs embedded in the Fabric Portal, so it has no addressable URL of its own.
The address bar belongs to the portal, and cross-origin restrictions prevent the app from
reading or writing the parent frame's location. There is therefore no supported way to
pass state into an app at launch, or to reflect the app's current state in a link.
This removes deep linking entirely — a capability every non-embedded web application has,
and one that users of an analytical app expect.
Concretely, none of the following are possible today:
We are building an analytical client for Power BI Semantic Models as a Fabric App. Sharing
a saved analysis with a colleague is one of its core workflows. Without deep linking, the
best we can offer is: the user copies a short identifier, sends it in a chat message, and
the recipient opens our app and pastes it into a dialog. That is a poor experience next to
sending a link, and it is the kind of friction users read as the platform being immature
rather than as a deliberate constraint.
Proposed solution
Extend the existing embedded-host postMessage bridge (@microsoft/fabric-embedded-host) with
a small state channel. The transport already exists, so this should be an addition to a
known protocol rather than new infrastructure.
Both directions are needed. Reading alone is not sufficient: without the ability to write,
nothing can produce a shareable link for the read side to consume.
READ — launch state
const state = await fabric.app.getLaunchState();
// => { view: "sales-by-region", filter: "AT", period: "2026-Q2" }
A plain key/value object supplied by the host, taken from the portal URL. Available
before first render so the app can restore without a visible flash of default state.
WRITE — reflect state in the portal URL
fabric.app.setState({ view: "sales-by-region", filter: "AT" }); // push
fabric.app.replaceState({ ... }); // replace
The host owns encoding, escaping and how the state appears in its URL. The app supplies
an opaque object; it never manipulates the URL directly.
CHANGE NOTIFICATION
fabric.app.onStateChange((state) => { ... });
So browser back/forward can work within an app rather than navigating the portal away
from it, which is the current behaviour and is easy for a user to trigger accidentally.
Security considerations, since they are the likely objection:
interpretation, so there is no injection path into portal chrome.
needing more can store the payload themselves and put an identifier in the URL, which is
exactly what a size limit should push people toward.
raises concerns; please consider shipping them separately rather than neither.
Alternatives considered
Identifier plus manual entry (what we are building now)
State is persisted in the app's own database and the user copies a short identifier that
the recipient pastes into an "Open by identifier" dialog. It works, but it replaces a
single click with copy, paste, and a dialog, and it fails the most common case entirely:
sending someone a link in a chat message.
Reading the parent frame's URL directly
Not possible cross-origin, and it would be the wrong approach even if it were — apps
should not be reading host chrome.
Encoding state in the app item's own configuration
Makes state global to the item rather than per link, so two users cannot hold different
states at once. Unusable for sharing.
A separate share service outside the app
Adds infrastructure to solve a problem the platform is better placed to solve once, and
every Fabric App with shareable state would have to build it independently.
Additional context
Precedent within the platform: Power BI report URLs already carry deep-link state such as
report bookmarks and filter parameters, and Fabric item URLs already carry item identifiers.
The concept exists in the portal; Fabric Apps simply have no access to it.
We are happy to test a preview implementation against a real workload — a data-heavy
analytical app where shared, reproducible state is central rather than incidental.