Unlock a world of possibilities! Login now and discover the exclusive benefits awaiting you.
One question that comes up repeatedly for teams embedding Qlik Sense into a broader product — rather than using it as a standalone BI tool — is how much of Qlik Sense to expose directly to end users versus how much to wrap in a custom layer. Having worked through a few of these integrations, I wanted to share a practical walkthrough covering both approaches: embedding via the Capability APIs, and building a custom visualization extension when the built-in chart types don't fit the use case.
Before writing any code, it's worth being clear on which problem you're actually solving, because the two approaches serve different goals:
Most real-world integrations end up using a combination of both.
The Capability API gives you programmatic access to Qlik Sense objects from outside the standard client. The core pattern looks like this:
require.config({
baseUrl: "https://<your-qlik-server>/resources"
});
require(["js/qlik"], function (qlik) {
var app = qlik.openApp("your-app-id", config);
app.getObject("QV01", "chart-object-id").then(function () {
console.log("Chart rendered into container QV01");
});
});
A few practical lessons from doing this in production:
Authentication is usually the first blocker, not the code itself. If you're on Qlik Sense Enterprise on Windows, you're typically dealing with ticketing or a reverse proxy handling session authentication before the mashup ever loads. On Qlik Cloud, this shifts to OAuth or web integration IDs. Get authentication working with a single static object first before building out the full mashup — debugging auth issues through a half-built UI wastes far more time than isolating it early.
Selections state needs a deliberate strategy. If your custom app has its own filter UI sitting outside Qlik Sense's native selection bar, you need to decide whether selections flow one-directionally (your UI → Qlik) or bidirectionally. The app.field("FieldName").select() method handles the former cleanly; the latter requires listening to app.getList("CurrentSelections", ...) and keeping your own UI state in sync, which adds real complexity and is worth avoiding unless the product genuinely needs it.
Object sizing inside a responsive layout is a recurring pain point. Qlik Sense objects rendered via getObject() don't automatically respond to container resizing the way you'd expect from a typical web component. Wrapping the container in a resize observer and calling the object's resize method explicitly tends to be more reliable than relying on CSS alone.
When the visualization requirement goes beyond what native charts support — a domain-specific chart type, a non-standard interaction pattern, or a layout that needs to combine multiple hypercubes — a custom extension is the better path.
A minimal extension definition looks like this:
define(["qlik", "jquery"], function (qlik, $) {
return {
initialProperties: {
qHyperCubeDef: {
qDimensions: [],
qMeasures: [],
qInitialDataFetch: [{ qWidth: 10, qHeight: 1000 }]
}
},
definition: {
type: "items",
component: "accordion",
items: {
dimensions: { uses: "dimensions", min: 1, max: 1 },
measures: { uses: "measures", min: 1, max: 3 },
sorting: { uses: "sorting" },
settings: { uses: "settings" }
}
},
paint: function ($element, layout) {
var hc = layout.qHyperCube;
// Render logic based on hc.qDataPages here
return qlik.Promise.resolve();
}
};
});
A few things that make the difference between an extension that works in a demo and one that survives real usage:
Respect the hypercube's paging model from the start. qInitialDataFetch limits what's loaded initially, and large datasets will require handling qDataPages fetches beyond that initial page. Extensions that assume the full dataset is available in paint() tend to break silently once someone applies the extension to a dataset larger than what was used during development.
The properties panel is worth investing time in, not just the paint function. A visually polished chart with a confusing or missing configuration panel gets abandoned by end users quickly. Using the standard uses: "dimensions", uses: "measures", and uses: "sorting" panel components keeps the extension's configuration experience consistent with native Qlik Sense objects, which matters more for adoption than most developers initially expect.
Selections inside a custom extension need explicit handling. Unlike native objects, a custom extension's click/selection behavior isn't automatic — you need to call app.field(...).select() or use the Backend selection API deliberately in response to user interaction, and manage the visual "selected" state yourself in the paint function.
If the goal is to surface existing Qlik Sense analytics inside a broader product experience, start with the Capability API mashup approach — it's faster to ship and doesn't require maintaining custom extension code across Qlik Sense version upgrades. If a specific visualization genuinely can't be built with native chart types, invest in a custom extension for that piece specifically, rather than defaulting to "build everything custom." Teams that lean too far toward custom extensions for functionality Qlik Sense already handles natively tend to take on maintenance burden that isn't necessary.
This pattern of combining a lightweight mashup shell with a small number of purpose-built extensions has worked well across a few implementations our team at Dev Technosys has been involved in, particularly where Qlik Sense needed to sit inside an existing product rather than stand alone as a separate BI tool.
Happy to go deeper on any specific piece of this — authentication flow, hypercube paging strategy, or extension property panel design — if it's useful for what others here are working through.