Embed a Qlik Sense Sheet using Capability API

Contributed by Daniel Pilla


Note: This tutorial leverages the Capability API , however the same method can be achieved with nebula.js provided that the visualizations are available.

In this tutorial, you are going to learn how to embed a sheet from a Qlik Sense application using the Capability API instead of embedding the sheet using an iframe. While an iframe is an easy mechanism for embedding content, there may be circumstances related to security, responsive design, and more where rendering the sheet with JavaScript is a better fit.

A sheet in a Qlik Sense application is a container for one-to-many visualizations, and the sheet's layout is accessible metadata in JSON that you can read to recreate the sheet programmatically. The benefit of this approach is that the layout of the sheet is created dynamically in the client and propagated to your web application without using an iframe.



The code example in this tutorial provides a basic, fully functional web page. To use this example page, ensure you have the following:

  1. An existing application ID and sheet ID to embed.
  2. A web integration configured which contains the location of the web page that will be embedding the Qlik Cloud application.
  3. A content security policy entry with the frame-ancestors directive added for the location of the web page that will be embedding the Qlik Cloud application.

To configure the code, the following variables from the sample code must be filled in:

const TENANT = '<tenant>.<region>.qlikcloud.com';
const WEBINTEGRATIONID = '<web-integration-id>';
const APPID = '<app-id>';
const SHEETID = '<sheet-id>';
const IDENTITY = '<identity';

Note: The IDENTITY > parameter is an arbitrary string to establish a separate session state. This can be used across iframes/varying methods of embedding to tie and/or separate sessions accordingly. It is an optional parameter included in this example for illustrative purposes.


The code in this sample ultimately does the following:

  1. Handles logging into Qlik.
  2. Loads the Capability API resources.
  3. Connects to the application.
  4. Gets the layout of the sheet.
  5. Sets a target div with a CSS grid that matches the sheet's layout.
  6. Iterates over each object within the sheet layout creating a div for each and setting it with the same grid position as in the layout. This process also sets the id of each div to the objectId of the Qlik object.
  7. Iterates over each div and injects each Qlik object whos objectId that matches the corresponding id.

Sample code

The sample code uses jQuery to showcase sheet rendering with the Capability API, however, it's not a required component for the solution. In addition, the below code includes sample handling for lack of browser support for third-party cookies. Refer to this article for more information.


    <script src="https://code.jquery.com/jquery-3.6.0.min.js"
        integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>


    <div id="sheet" style="height:800px;">



        //    CONFIGURATION

        const TENANT = '<tenant>.<region>.qlikcloud.com';
        const WEBINTEGRATIONID = '<web-integration-id>';
        const APPID = '<app-id>';
        const SHEETID = '<sheet-id>';
        const IDENTITY = '<identity>';


        const qlikDivSelector = '#sheet';
        const qlikObjectClass = 'qlikObject';

        //    MAIN

        (async function main() {
            const isLoggedIn = await qlikLogin();
            const loadedCapabilitiesAssets = await loadCapabilitiesAssets();
            const app = await doCapabilities(APPID, qlikDivSelector, qlikObjectClass);

        //    LOGIN

        async function qlikLogin() {
            const loggedIn = await fetch(`https://${TENANT}/api/v1/users/me`, {
                mode: 'cors',
                credentials: 'include',
                headers: {
                    'qlik-web-integration-id': WEBINTEGRATIONID,
            if (loggedIn.status !== 200) {
                if (sessionStorage.getItem('tryQlikAuth') === null) {
                    sessionStorage.setItem('tryQlikAuth', 1);
                    window.location = `https://${TENANT}/login?qlik-web-integration-id=${WEBINTEGRATIONID}&returnto=${location.href}`;
                    return await new Promise(resolve => setTimeout(resolve, 10000)); // prevents further code execution
                } else {
                    const message = 'Third-party cookies are not enabled in your browser settings and/or browser mode.';
                    throw new Error(message);
            console.log('Logged in!');
            return true;

        // CREATE GRID

        function createGrid(querySelector, columns, rows) {
            const gridContainer = document.querySelector(querySelector);
            gridContainer.style.display = 'grid';
            gridContainer.style.gridTemplateRows = `repeat(${rows}, minmax(0, 1fr)`;
            gridContainer.style.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`;

            return gridContainer;


        function constructSheet(sheetLayout, querySelector, addClass) {
            const gridContainer = createGrid(querySelector, sheetLayout.columns, sheetLayout.rows);
            (sheetLayout.cells).forEach(function (object) {
                const objectId = object.name;
                const colStart = object.col + 1;
                const rowStart = object.row + 1;
                const colSpan = object.colspan;
                const rowSpan = object.rowspan;
                const objectDiv = document.createElement('div');
                objectDiv.id = objectId;
                objectDiv.style["grid-column"] = `${colStart} / span ${colSpan}`;
                objectDiv.style["grid-row"] = `${rowStart} / span ${rowSpan}`;
                objectDiv.style.border = '2px solid #0000';
                objectDiv.style.margin = '5px';
            return true;


        async function loadCapabilitiesAssets() {
            const cssUrl = `https://${TENANT}/resources/autogenerated/qlik-styles.css`;
            const requireUrl = `https://${TENANT}/resources/assets/external/requirejs/require.js`;
            return $.when(
                $('head').append(`<link rel="stylesheet" type="text/css" href="${cssUrl}">`),

        async function doCapabilities(appId, qlikDivSelector, objectClass) {
            var config = {
                host: TENANT,
                prefix: "/",
                port: 443,
                isSecure: true,
                webIntegrationId: WEBINTEGRATIONID,
                identity: IDENTITY

                config: {
                    text: { useXhr: function (url, protocol, hostname, port) { return true; } }
                baseUrl: 'https://' + config.host + (config.port ? ':' + config.port : '') + config.prefix + 'resources',
                webIntegrationId: WEBINTEGRATIONID

            requirejs(["js/qlik"], (qlik) => {
                const app = qlik.openApp(appId, config);
                const sheetObject = app.getObject(SHEETID)
                    .then((sheetObject) => {
                        const sheetLayout = sheetObject.getLayout();
                        constructSheet(sheetLayout.$$state.value, qlikDivSelector, objectClass);
                        $(`.${objectClass}`).each(function () {
                            var chartId = $(this).attr("id");
                            app.getObject(this, chartId);



Was this page helpful?