How to embed Tableau views into web applications — Connected Apps authentication, the Embedding API, single sign-on architecture, RLS for multi-tenant data isolation, and the deployment considerations that separate production-ready embedded analytics from development prototypes.
Embedded analytics — Tableau views built into a web application, customer portal, or internal tool — is one of the highest-value use cases for the Tableau platform. Done well, it gives end users analytics in context, within the systems they already use, without requiring them to navigate to a separate BI tool. Done poorly, it produces a security gap, a performance problem, or a maintenance burden that the data team did not anticipate.
This guide covers the production architecture of Tableau embedded analytics — authentication, the Embedding API, row-level security for multi-tenant data, and the deployment considerations that distinguish a production implementation from a development prototype.
The Embedding Architecture
Tableau embedded analytics consists of three components:
**The Tableau Server or Tableau Cloud environment** that hosts the views to be embedded. The views are standard Tableau workbooks — published to a site, with permissions configured for the embedding use case.
**The host application** — the web application or portal in which Tableau views will appear. The host application manages user authentication, generates the embedding context, and renders the Tableau view within its UI.
**The Embedding API** (currently v3, formerly JavaScript API) — the JavaScript library that the host application uses to instantiate and control Tableau views within the browser. The API handles communication between the host application and the Tableau Server environment.
Authentication with Connected Apps
The primary authentication mechanism for modern Tableau embedded deployments is Connected Apps — a Tableau-native mechanism that allows the host application to generate signed JWT (JSON Web Token) credentials that authenticate users to Tableau without requiring Tableau-managed user accounts.
Connected Apps authentication flow:
1. The user authenticates to the host application through whatever authentication mechanism the application uses (SSO, OAuth, password — this is independent of Tableau)
2. The host application's server-side code generates a JWT signed with the Connected Apps secret
3. The JWT is passed to the Tableau Embedding API as a credential
4. Tableau validates the JWT, associates the request with the Tableau user identified in the JWT, and returns the embedded view
The JWT contains claims: the Tableau username, the Tableau site, expiration time, and a unique JTI (JWT ID) to prevent replay attacks. The JWT is short-lived (typically 5–10 minutes) and generated server-side — the Connected Apps secret must never be exposed to the browser.
Connected Apps supports two modes: **default access control** (the embedded user must have a valid Tableau account) and **direct trust** (the host application's identity server is trusted and Tableau creates or maps accounts automatically). Direct trust is appropriate for customer-facing embedding where you do not want to manually provision Tableau accounts for every customer.
The Embedding API
The Tableau Embedding API v3 is the JavaScript interface for rendering and controlling Tableau views in a host application. Installation via npm or CDN:
import { TableauViz, TableauEventType } from '@tableau/embedding-api';
Instantiating a view:
const viz = new TableauViz();
viz.src = 'https://your-tableau-server/views/WorkbookName/ViewName';
viz.token = jwtToken; // JWT from Connected Apps
document.getElementById('tableau-container').appendChild(viz);
The Embedding API v3 uses a custom HTML element (tableau-viz) rather than the iframe-based approach of v1/v2. This provides better performance and allows direct DOM interaction without cross-origin iframe communication.
**Programmatic filter control:** Apply filters to the embedded view in response to application state — filtering a Tableau view to the current user's organisation, the selected date range, or the selected product:
const sheet = viz.workbook.activeSheet;
await sheet.applyFilterAsync('Region', ['North', 'East'], FilterUpdateType.Replace);
**Parameter control:** Set Tableau parameters that control view behaviour — switching between metrics, changing chart types, or setting calculation parameters:
await viz.workbook.changeParameterValueAsync('Selected Metric', 'Revenue');
**Event handling:** Listen for user interactions within the embedded view:
viz.addEventListener(TableauEventType.MarkSelectionChanged, async (event) => {
const marks = await event.getMarksAsync();
// Handle the selection in the host application
});
**Toolbar configuration:** Show or hide specific toolbar elements (undo, redo, revert, refresh, download, share) based on what is appropriate for the embedding context. Embedded analytics in a customer portal typically shows fewer controls than an internal analytical tool.
Row-Level Security for Multi-Tenant Embedding
The critical security requirement for customer-facing embedded analytics: a customer should only see their own data. Tableau row-level security must ensure that when Customer A is logged in, they see only Customer A's data — regardless of what view or parameters they access.
**User filter approach:** Create a calculated field in the data source that compares the current Tableau username to a user identifier in the data. For a customer portal where each Tableau account represents a customer:
IF ISMEMBEROF('Admins') THEN TRUE
ELSEIF USERNAME() = STR([customer_id])
THEN TRUE
ELSE FALSE
END
Apply this as a user filter on the relevant dimension. Every user sees only rows where customer_id matches their Tableau username.
**Row-level security with Connected Apps:** When using Connected Apps direct trust, the Tableau username in the JWT is set by the host application. For a customer portal, set the username to the customer's unique identifier. The user filter then filters to only that customer's data automatically.
**Entitlements table approach:** For complex permission models (a user can access multiple customers, a customer has multiple departments with different access levels), maintain an entitlements table in the data source that maps users to the rows they can access. The row-level security calculation joins to this table:
[Username] IN (
SELECT [allowed_user]
FROM entitlements_table
WHERE [record_id] = [data_table].[id]
)
This approach requires a data source that can express the join (published data source or custom SQL).
**Initial SQL for session context:** Snowflake and other warehouses support session context variables. Tableau's Initial SQL (data source configuration) can set these variables at connection time:
CALL sp_set_user_context('TABLEAU_USER', TABLEAU_USER());
The warehouse-level row policies can then use this session variable to filter data without any Tableau-side calculation. This moves the security enforcement to the database layer, which is more robust than Tableau-side filters.
Performance Considerations
**Published certified data sources.** Embedded views should always use certified published data sources, not workbook-embedded data sources. Published sources are cached centrally and shared across sessions. Workbook-embedded sources create separate connections per session, multiplying database load.
**VizQL server session management.** Each embedded view initialisation creates a VizQL server session on Tableau Server. A page with 5 embedded views creates 5 sessions. On Tableau Cloud, this is managed automatically. On Tableau Server, ensure your VizQL server process count can handle the concurrent embedded session load.
**Thumbnail caching.** Embedded views that are not personalised (the same view for every user) benefit from Tableau's render cache. Views filtered by user identity cannot be cached across users — each user's view is unique. Design the embedding so that view-level chrome (navigation, header) is cached and only the personalised data layer requires individual rendering.
**Lazy loading.** Do not instantiate all embedded views on page load. Use the Embedding API's lazy loading option or use IntersectionObserver to initialise views only when they scroll into the viewport. This reduces initial page load time and concurrent VizQL session load.
Deployment Considerations
**Content URL configuration.** The embedded view URL is determined by the view's content URL in Tableau Server. If the workbook or view is renamed, the URL changes and the embedding breaks. Use immutable content URLs (set at publish time) and avoid renaming published workbooks in production.
**Allowed domains.** Configure the Tableau Server or Cloud site's allowed domains for embedding. Tableau blocks embedding in iframes from domains not on the allowlist. List all production, staging, and development domains that will embed Tableau views.
**CSP headers.** Your host application's Content Security Policy must allow the Tableau Server domain as a frame-src. If CSP is strict (which it should be for customer-facing applications), add the Tableau domain explicitly.
**Error handling.** Embedded views fail gracefully in different ways: JWT expiry produces a re-authentication redirect, permission errors produce an "access denied" state, network errors produce a disconnected state. The Embedding API provides events for each failure mode. Implement host-application-level error handling that catches these events and presents appropriate UX to the user — not a broken iframe.
Our Tableau consulting practice implements embedded analytics deployments from authentication architecture through production hardening — contact us to discuss your embedded analytics requirements.
A former Microsoft data architect audits your data foundation, identifies your top priorities, and sends you a written plan. Free. No pitch.
Book a Call →