With crm-powerbi-viewer you can add custom logic to any Power BI view.

First you put your filter and event handling logic in functions inside one or more scripts (web resources). These you reuse across embedded views by referencing the functions. You tell crm-powerbi-viewer about your scripts by referencing them in the crm-powerbi-viewer application config.


To call custom logic you need to:

  1. Create a javascript webresource with a function that takes the embedded Power BI object as first and only argument. In this function you add filters and/or event handlers to the embedded object (see below for a sample function).
  2. Add a reference to the new web resource in crm-powerbi-viewer config:
    1. Open the power bi viewer config file and locate custom_scripts
    2. Change null to an array of scripts to load. It is recommended that these scripts are web resources

      var customConfig = { ...
         custom_scripts: ["samples/filters.js?ver=1", "../../prefix_/scripts/eventhandlers.js?ver=1"]

      ?ver=1 added so cache can be invalidated on new versions. Currently scripts are not cached but that may change in a future release. Increase the value when releasing updates to the script.

      It is recommended to use relative urls. They are relative to viewer.html.

  3. Then add &customFn=<functionname> to the web resource custom parameter (dot notation is supported on functionname).

You can have multiple functions in a script file and each embedded view may call different functions. Thus various filters and events can be set for each view.

Sample functions

Filter report page by user name

window.Samples = window.Samples || {};
Samples.Filters = Samples.Filters || {};

 * Filter on user by name whenever a specific page is shown.
 * Note: This resets the filter whenever the user navigates to the specific page. 
 * Remove the event logic and set the filter once to avoid resetting on every navigation.
 * Suggested modifications:
 * - In your own code you should check against page name and not displayname to avoid 
 *   breaking the code if someone changes the displayname
 * - Filters should be on ID and not name
 * - Using Xrm get name/id from current user or owner if view is embedded to a form.
Samples.Filters.filterOnUser = function (report) {

    report.on("loaded", function (loadedEvent) {
        report.removeFilters(); // For some reason this needs to be done before setting page filters (August 2018)

        report.on("pageChanged", function(pageChangedEvent) {
            let page = pageChangedEvent.detail.newPage;
            console.log(`Page changed to '${page.displayName}' (${page.name})`);

            if (page.displayName === "Top Won/Lost Deals") {
                const filter = {
                    $schema: "http://powerbi.com/product/schema#basic",
                    target: {
                        table: "User",
                        column: "Full Name"
                    operator: "In",
                    values: ["Trond Aarskog"]


Catch tile clicks and open full Power BI report

window.Samples = window.Samples || {};
Samples.Filters = Samples.Filters || {};

 * Open full report in Power BI when a tile is clicked.
 * Note: It is possible to open the report in a dialog - 
 *       similar to the OOB experience but it is an unsupported
 *       hack that may break on future D365 updates (and might 
 *       not even work in the new unified interface). You need to
 *       investigate how to do that yourself but a hint is Mscrm.CrmDialog...
 *       The sample below uses the supported approach :)
Samples.Filters.openOnTileClick = function (embed) {
    embed.on("tileClicked", function(event) {
        // If you want to open in a new window

        // Alternative: Navigate to Power BI (and away from D365CE)
        // top.location.replace(event.detail.navigationUrl);