Integrate Bolt.new with Tableau using two approaches: embed Tableau views directly in your app using the Tableau JavaScript Embedding API (iframe-based, works in Bolt's WebContainer for published Tableau Server or Tableau Cloud views), or query workbooks and data sources via the Tableau REST API through a Next.js API route. Embedding is dramatically simpler and requires no backend — start there unless you need to read or modify Tableau metadata programmatically.
Embedding Tableau Dashboards and Querying Tableau Data in Bolt.new Apps
Tableau offers two distinct integration surfaces for developers. The Embedding API (version 3, using the tableau-viz web component) lets you render live, interactive Tableau dashboards directly inside any web app using a URL and a few lines of JavaScript. Users see and interact with the actual Tableau viz — filtering, drilling down, highlighting — without leaving your app. This approach works entirely in the browser, makes no server-side calls, and is fully functional in Bolt's WebContainer development preview.
The Tableau REST API is the second integration surface. It gives you programmatic access to Tableau Server or Tableau Cloud's administrative layer: listing workbooks, managing users, refreshing data sources, reading view metadata, and downloading data. This is server-side only (your credentials must never reach the browser), and it is most useful for building custom catalogs of available dashboards, automating workbook management, or extracting Tableau data to use in non-Tableau visualizations.
For most Bolt.new use cases — adding analytics dashboards to an existing app — the Embedding API is the right starting point. It requires nothing more than a public or authenticated Tableau view URL and the tableau-viz web component script. The REST API adds significant complexity and is only warranted when you need to query the Tableau catalog or automate server administration tasks.
Integration method
Bolt generates Tableau integration code through conversation. For the simpler embedding path, Bolt writes React components that use Tableau's JavaScript Embedding API to render Tableau views inline — no backend required, works directly in the Bolt preview. For the REST API path (workbook metadata, data source management), Bolt generates Next.js API routes that authenticate with Tableau Server or Tableau Cloud and call the REST API server-side.
Prerequisites
- Access to Tableau Server (on-premises) or Tableau Cloud (SaaS) with at least Viewer permissions
- A published Tableau view or workbook with a shareable URL (for embedding) or REST API access enabled
- For REST API: a Personal Access Token created in your Tableau account (My Account → Personal Access Tokens)
- Your Tableau Server URL or Tableau Cloud URL (e.g., https://prod-us-useast-1.online.tableau.com)
- For Tableau Cloud: your site name from the URL (the path segment after /site/)
Step-by-step guide
Embed a Tableau view using the JavaScript Embedding API
Embed a Tableau view using the JavaScript Embedding API
The simplest Tableau integration requires zero backend setup. Tableau's Embedding API v3 provides a custom HTML element — tableau-viz — that renders any Tableau view inline in your app. You only need to load the Tableau JavaScript library and place the element in your component. To find the embed URL for a view, open it in Tableau Server or Tableau Cloud, click the Share icon, and copy the embed link. It looks like https://your-tableau-server/views/WorkbookName/ViewName. This URL is used as the src attribute of the tableau-viz element. For publicly accessible views (or views with Tableau's built-in authentication), the embed works without any token. For authenticated views that require users to be logged in, you have two options: embed with a token (requires a ticket from your server), or configure trusted authentication on Tableau Server. For development testing, the simplest approach is to publish a view as publicly accessible or test with your own Tableau session cookie already in the browser. The tableau-viz element supports a rich set of attributes and JavaScript events that let you filter the view, listen for selection changes, and control toolbar options. All of this is client-side JavaScript that works perfectly in Bolt's WebContainer preview — no server-side code required for basic embedding.
Add a Tableau dashboard section to my Next.js app. Load the Tableau Embedding API v3 JavaScript library from the CDN and create a React component called TableauEmbed that renders a tableau-viz custom element with configurable src URL and dimensions. Add a toolbar option to show only Export. Make the embed responsive with a fixed aspect ratio wrapper.
Paste this in Bolt.new chat
1// components/TableauEmbed.tsx2'use client';3import { useEffect, useRef } from 'react';45interface TableauEmbedProps {6 viewUrl: string;7 height?: number;8 filters?: Record<string, string>;9 token?: string; // Optional: Connected App token for SSO10}1112export function TableauEmbed({13 viewUrl,14 height = 600,15 filters = {},16 token,17}: TableauEmbedProps) {18 const containerRef = useRef<HTMLDivElement>(null);1920 useEffect(() => {21 // Load Tableau Embedding API v322 if (!document.querySelector('script[src*="tableau.embedding.3"]')) {23 const script = document.createElement('script');24 script.type = 'module';25 script.src =26 'https://embedding.tableauusercontent.com/tableau.embedding.3.latest.min.js';27 document.head.appendChild(script);28 }29 }, []);3031 // Build filter parameters as URL query string32 const filterParams = Object.entries(filters)33 .map(([key, value]) => `:${key}=${encodeURIComponent(value)}`)34 .join('&');3536 const finalUrl = filterParams ? `${viewUrl}?${filterParams}` : viewUrl;3738 return (39 <div ref={containerRef} style={{ width: '100%' }}>40 {/* @ts-ignore — custom element not in TS types */}41 <tableau-viz42 src={finalUrl}43 height={`${height}px`}44 width="100%"45 toolbar="bottom"46 hide-tabs47 {...(token ? { token } : {})}48 />49 </div>50 );51}5253// Usage in a page:54// <TableauEmbed55// viewUrl="https://public.tableau.com/views/WorldIndicators/GDPpercapita"56// height={700}57// filters={{ 'Region': 'Europe' }}58// />Pro tip: Use Tableau Public views (public.tableau.com) for testing the embed component — they require no authentication and work immediately. Switch to your Tableau Server URLs after confirming the embedding works.
Expected result: The TableauEmbed component renders a fully interactive Tableau visualization inline in your app. The toolbar shows Export options and users can interact with the viz using filters and tooltips.
Add server-side authentication for private Tableau views
Add server-side authentication for private Tableau views
For Tableau Server views that require authentication, the recommended approach is Tableau Connected Apps — a server-side mechanism where your server generates a signed JSON Web Token that Tableau validates. This is similar to SSO and lets your users access embedded views without a separate Tableau login. For Tableau Cloud, Connected Apps are configured under Settings → Connected Apps → New Connected App. You get a Client ID and Secret Key. On your server, generate a JWT signed with the secret, include it in the tableau-viz element's token attribute, and Tableau will authenticate the user seamlessly. For simpler cases where you are embedding for internal users who already have Tableau accounts, Tableau Server's trusted authentication works without JWT: your server requests a 'ticket' from Tableau by posting the user's Tableau username to the trusted URL endpoint, and the ticket is appended to the view URL (/trusted/{ticket}/views/...). The ticket is valid for three minutes — enough time to render the embedded view. For the REST API path in the next step, you use a Personal Access Token instead — a separate mechanism from embedding authentication. Do not confuse the two: embedding tokens are for rendering views, REST API tokens are for administrative API calls.
Create a /api/tableau/embed-token route that generates a Tableau Connected App JWT token for the current user. Use the TABLEAU_CONNECTED_APP_CLIENT_ID and TABLEAU_CONNECTED_APP_SECRET_KEY environment variables. The JWT should include the user email from the session, be signed with HS256, and expire in 10 minutes. Return the token to the client for use in the tableau-viz token attribute.
Paste this in Bolt.new chat
1// app/api/tableau/embed-token/route.ts2import { NextResponse } from 'next/server';3import { SignJWT } from 'jose';4import { randomUUID } from 'crypto';56export async function GET() {7 const clientId = process.env.TABLEAU_CONNECTED_APP_CLIENT_ID!;8 const secretKey = process.env.TABLEAU_CONNECTED_APP_SECRET_KEY!;9 const secretId = process.env.TABLEAU_CONNECTED_APP_SECRET_ID!;1011 const secret = new TextEncoder().encode(secretKey);1213 // JWT claims required by Tableau Connected Apps14 const token = await new SignJWT({15 jti: randomUUID(),16 iss: clientId,17 sub: 'user@yourcompany.com', // Replace with actual user identifier18 aud: 'tableau',19 scp: ['tableau:views:embed'], // Embedding scope20 })21 .setProtectedHeader({ alg: 'HS256', kid: secretId, iss: 'tableau' })22 .setIssuedAt()23 .setExpirationTime('10m')24 .sign(secret);2526 return NextResponse.json({ token });27}Pro tip: Install the jose package for JWT signing: it works in Next.js Edge Runtime and Node.js environments. Run 'npm install jose' in your Bolt terminal.
Expected result: The /api/tableau/embed-token route returns a signed JWT. Passing this token to the TableauEmbed component's token prop allows authenticated Tableau views to render without a separate login prompt.
Query the Tableau REST API for workbooks and views
Query the Tableau REST API for workbooks and views
The Tableau REST API is version-specific: the base URL is https://your-tableau-server/api/{api-version} where the current version is 3.22 for Tableau Server 2024.3 and Tableau Cloud. The API uses XML by default but accepts JSON when you include Accept: application/json headers. Authentication uses a Personal Access Token. First, sign in by POSTing to /auth/signin with your Personal Access Token name and value. The response includes an auth token and a siteId. Include the auth token in all subsequent requests as the x-tableau-auth header. Auth tokens expire after 4 hours — implement refresh logic for long-running processes. For Tableau Cloud, the site name is required in the sign-in request body. Find it in the URL when logged into Tableau Cloud: it is the segment after /site/ in the URL. For Tableau Server, the site name is typically empty string for the default site. Common REST API operations: GET /sites/{siteId}/workbooks returns all workbooks the authenticated user has access to. GET /sites/{siteId}/workbooks/{workbookId}/views returns all views in a workbook with their view names, content URLs, and thumbnail URLs. GET /sites/{siteId}/datasources returns data sources. These responses work well as data sources for your custom analytics catalog.
Create Tableau REST API integration for my app. Add a /api/tableau/auth route that signs in using a Personal Access Token and caches the auth token. Create /api/tableau/workbooks that returns all workbooks the authenticated user can access. Store TABLEAU_SERVER_URL, TABLEAU_SITE_NAME, TABLEAU_PAT_NAME, and TABLEAU_PAT_SECRET in the .env file.
Paste this in Bolt.new chat
1// lib/tableau-rest.ts2interface TableauSession {3 token: string;4 siteId: string;5 expiresAt: number;6}78let session: TableauSession | null = null;910const BASE_URL = process.env.TABLEAU_SERVER_URL!; // e.g., https://prod-useast.online.tableau.com11const SITE_NAME = process.env.TABLEAU_SITE_NAME ?? ''; // Empty string for default site12const API_VERSION = '3.22';1314export async function getTableauSession(): Promise<TableauSession> {15 if (session && Date.now() < session.expiresAt - 300_000) return session;1617 const response = await fetch(`${BASE_URL}/api/${API_VERSION}/auth/signin`, {18 method: 'POST',19 headers: {20 'Content-Type': 'application/json',21 Accept: 'application/json',22 },23 body: JSON.stringify({24 credentials: {25 personalAccessTokenName: process.env.TABLEAU_PAT_NAME!,26 personalAccessTokenSecret: process.env.TABLEAU_PAT_SECRET!,27 site: { contentUrl: SITE_NAME },28 },29 }),30 });3132 if (!response.ok) throw new Error(`Tableau sign-in failed: ${response.status}`);3334 const data = await response.json();35 session = {36 token: data.credentials.token,37 siteId: data.credentials.site.id,38 expiresAt: Date.now() + 4 * 60 * 60 * 1000, // 4 hours39 };40 return session;41}4243// app/api/tableau/workbooks/route.ts44import { NextResponse } from 'next/server';45import { getTableauSession } from '@/lib/tableau-rest';4647export async function GET() {48 const { token, siteId } = await getTableauSession();4950 const response = await fetch(51 `${process.env.TABLEAU_SERVER_URL}/api/3.22/sites/${siteId}/workbooks?pageSize=100`,52 {53 headers: {54 'x-tableau-auth': token,55 Accept: 'application/json',56 },57 }58 );5960 const data = await response.json();61 return NextResponse.json({62 workbooks: data.workbooks?.workbook ?? [],63 total: data.workbooks?.pagination?.totalAvailable ?? 0,64 });65}Pro tip: Tableau's REST API paginates results with pageNumber and pageSize parameters. The default page size is 100 with a maximum of 1000. For large Tableau deployments with hundreds of workbooks, implement pagination using the totalAvailable field in the pagination response.
Expected result: Calling /api/tableau/workbooks returns a JSON array of workbooks the authenticated user can access, with names, project names, owner information, and content URLs for building embed links.
Common use cases
Embedded analytics in a SaaS dashboard
Add a Tableau analytics section to your SaaS product so customers can view their usage data without leaving your app. The Tableau view filters automatically to show only the authenticated user's data using Tableau's URL filter parameters or trusted authentication with user context.
Add a Tableau dashboard section to my Next.js SaaS app. Embed a Tableau view using the tableau-viz web component. The view URL is [your Tableau view URL]. Add filter parameters to the URL to filter by customer_id which comes from the logged-in user's session. Display the viz in a full-width card below the main dashboard metrics.
Copy this prompt to try it in Bolt.new
Custom workbook catalog
Build an internal analytics portal that lists all available Tableau workbooks and views from your Tableau Server, organized by department and data source. Users click a workbook to open its embedded view without needing to navigate Tableau's own interface.
Create a Tableau workbook catalog page. Use /api/tableau/workbooks to fetch all workbooks from our Tableau Server REST API. Display them in a grid of cards with workbook name, project name, owner, and last updated date. Clicking a card opens the selected view embedded using the tableau-viz component.
Copy this prompt to try it in Bolt.new
Automated data refresh trigger
Build an admin panel that lets power users trigger a Tableau data source refresh on demand rather than waiting for the scheduled refresh cycle. The panel shows the last refresh time and status, and a button that calls the Tableau REST API to initiate a new refresh.
Build a data refresh admin page. Fetch the list of data sources from the Tableau REST API at /api/tableau/datasources. Show each data source with its name, last refresh time, and current status. Add a Refresh Now button for each that POSTs to /api/tableau/datasources/[id]/refresh.
Copy this prompt to try it in Bolt.new
Troubleshooting
tableau-viz component renders a blank white area with no error in the console
Cause: The Tableau Embedding API JavaScript has not finished loading when the component mounts, or the view URL is incorrect. Tableau's custom element is defined asynchronously after the module script loads.
Solution: Add a small delay before initializing the tableau-viz element, or listen for the tableauEmbeddingApiInit event before rendering the component. Also verify the view URL by opening it directly in a browser — if it requires authentication, the embedded view will be blank until a valid token is provided.
1// Wait for Tableau API to load before rendering:2useEffect(() => {3 window.addEventListener('tableauEmbeddingApiInit', () => {4 setApiReady(true);5 });6 // Also handle case where API was already loaded7 if (window.tableau) setApiReady(true);8}, []);Tableau REST API sign-in returns 401 with 'Invalid credentials'
Cause: The Personal Access Token name or secret is incorrect, the token has been revoked, or the site name in the request body does not match the Tableau site the token was created for.
Solution: In Tableau, go to My Account → Personal Access Tokens and verify the token name matches exactly. Check that TABLEAU_SITE_NAME in your .env matches the content URL shown in your Tableau URL. For Tableau Server's default site, use an empty string. For Tableau Cloud, the site name is the path segment between /site/ and the next slash in the URL.
Embedded Tableau view shows 'Permision Denied: You do not have permission to view this content'
Cause: The Tableau account used for authentication does not have Viewer or higher permissions on the specific workbook or view being embedded, or the view is restricted to specific users or groups in Tableau's content permissions.
Solution: Log into Tableau Server as an admin and check the workbook's permissions under the Permissions tab. Ensure the user or group that will view the embedded content has at least Viewer role. For Connected App embedding, verify the user email in the JWT sub field maps to a valid Tableau user with the necessary permissions.
Best practices
- Start with the embedding approach (tableau-viz web component) before attempting the REST API — embedding covers 80% of use cases and requires zero backend setup for publicly accessible views.
- Use Tableau Public (public.tableau.com) for development and testing of the embed component — public views work immediately without any authentication configuration.
- Cache the Tableau REST API session token in memory with its expiry time — sign-in requests count against API rate limits and tokens last 4 hours, so refreshing unnecessarily wastes both.
- Store all Tableau credentials (PAT name, PAT secret, Connected App secret) in server-side environment variables only — never in NEXT_PUBLIC_ variables or client-side code.
- Add filter parameters to embed URLs using Tableau's URL filter syntax (prefixed with a colon) to automatically scope dashboards to the current user's data: ?:customer_id=123.
- For Tableau Server on-premises, always specify the API version explicitly (api/3.22) rather than relying on a 'latest' URL pattern — REST API versions are tied to server versions and the version you develop against may not be available on all environments.
- If you only need to embed a single Tableau view without any programmatic control, use a standard HTML iframe with the view URL — the tableau-viz web component adds value when you need JavaScript interaction (filtering, event listeners, toolbar control).
Alternatives
Power BI Embedded is the best choice if your organization is in the Microsoft 365 ecosystem and wants to embed analytics with the same licensing already in place.
Looker provides a powerful embed API and LookML semantic layer that integrates deeply with data engineering workflows, preferred by engineering-led data teams.
Google Analytics with the Reporting API is a simpler alternative for web traffic and user behavior analytics without requiring a dedicated BI platform license.
Amplitude focuses on product analytics and user behavior with an embeddable chart API, making it a more targeted alternative for SaaS product teams.
Frequently asked questions
Can I embed Tableau views in Bolt's development preview without deploying?
Yes, for publicly accessible or session-authenticated views. The tableau-viz web component is a client-side library that makes requests from the user's browser directly to Tableau Server or Tableau Cloud. Since the requests originate from the browser (not from Bolt's WebContainer infrastructure), they work without any server-side configuration. Tableau Public views embed immediately with no setup at all.
Does Bolt.new have a native Tableau integration?
No. Tableau is not one of Bolt's native connectors. You integrate Tableau by loading the JavaScript Embedding API in your React components or building Next.js API routes that call the Tableau REST API. Bolt's AI can generate both types of integration code when you describe what you need.
What is the difference between the Tableau Embedding API and the Tableau REST API?
The Embedding API is for rendering interactive Tableau visualizations inside your web app — it runs in the browser and requires no backend. The REST API is for programmatic access to Tableau Server's administrative functions — listing workbooks, managing users, refreshing data sources — and requires server-side execution to protect credentials. Most Bolt.new integrations need only the Embedding API.
Do I need a Tableau license to embed views in my app?
Yes. For Tableau Server or Tableau Cloud, embedded viewers need a Tableau Viewer license or the workbook must be configured for guest access. For programmatic embedding in commercial products without per-user Tableau licenses, Tableau has a dedicated product called Tableau Embedded Analytics with separate licensing. Check with your Tableau account representative for the licensing model that fits your use case.
Can I filter an embedded Tableau view based on the logged-in user?
Yes, through two mechanisms. The simpler approach is URL filters: append filter parameters to the view URL using Tableau's URL filter syntax (?:field_name=value). The more secure approach is using Tableau Connected Apps where you embed user attributes in the JWT token — Tableau can then apply row-level security policies based on the user identity in the token, ensuring users only see data they have permission to access.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation