microsoft / microsoft/rayfin

Deep linking for Fabric Apps: read and write URL state from the embedded app

Open
#60 1 comment 2 reactions 1 assignee View on GitHub

@bczulauf is already working on this.

Since Jul 29, 2026.

enhancement issue-mapped triaged
Dominant language
No language data
Stars
610
Forks
62
Avg merge
1d 23h
Merged PRs (30d)
3

Description

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.

  1. 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.

  2. 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.

  3. 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
  1. 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.

  2. 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.

  3. 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.

  4. 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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.