Guide April 18, 2026 · 17 mins · The D23 Team

Embedded Superset Dashboards: SDK Walkthrough for Engineering Teams

Complete guide to embedding Superset dashboards with JWT auth, theming, and the embedded SDK. Build production analytics into your product.

Embedded Superset Dashboards: SDK Walkthrough for Engineering Teams

Embedded Superset Dashboards: SDK Walkthrough for Engineering Teams

Embedding analytics directly into your product is no longer a luxury—it’s table stakes for modern SaaS platforms. But embedding Superset dashboards comes with real technical decisions: authentication, row-level security, theming, and state management. This guide walks you through the entire process, from SDK setup to JWT tokens and custom styling, so your engineering team can ship embedded analytics without reinventing the wheel.

We’ll assume you already understand Apache Superset’s core concepts. If you need a refresher on dashboard creation, the Creating Your First Dashboard - Apache Superset Documentation is the canonical reference. For the purposes of this walkthrough, we’re focused on the embedding layer—how to securely serve dashboards inside your application with full control over branding and access.

The embedded Superset SDK is purpose-built for this use case. Unlike exporting static images or building custom query layers, the SDK gives you a thin, production-ready wrapper around Superset’s frontend that handles authentication, refresh logic, and interactive filtering. At D23, we’ve helped dozens of engineering teams integrate this pattern into their products, and the patterns we’ll cover here are battle-tested across scale-ups and mid-market companies.

Understanding the Embedded SDK Architecture

Before writing code, it’s worth understanding what the embedded SDK actually does. The Superset Embedded SDK is a lightweight JavaScript library that communicates with your Superset instance via a secure token-based protocol. Your backend generates a JWT token that grants temporary, scoped access to a specific dashboard. The SDK then renders that dashboard inside an iframe with all the interactive features—filtering, drilling, exporting—intact.

The key architectural principle is zero trust between your frontend and Superset. Your frontend never talks directly to Superset. Instead, your backend generates a signed JWT token that proves your user has permission to view a specific dashboard. This token is passed to the SDK, which uses it to authenticate with Superset. Superset validates the token’s signature, checks the expiration, and enforces any row-level security rules embedded in the token claims.

This design has several advantages:

  • No credential leakage: Your Superset API keys never leave your backend.
  • Fine-grained access control: You can encode user roles, data filters, and time-based restrictions directly in the token.
  • Stateless scaling: Each request is self-contained; no session state required.
  • Audit trail: Every dashboard view can be logged with the token’s claims.

The embedded SDK itself is maintained as part of the Apache Superset project. The Embedded Superset SDK README is the authoritative reference for installation, configuration, and API methods. We’ll walk through the most common patterns here, but that document is your north star for edge cases and version-specific behavior.

Setting Up Your Backend Token Generation

The first step is building a backend endpoint that generates JWT tokens. This is where your access control logic lives. Your frontend will call this endpoint, passing the user context and dashboard ID. Your backend validates the user’s permissions, constructs a JWT with the appropriate claims, and returns the token to the frontend.

Here’s a Python example using Flask and PyJWT. This assumes you’ve already configured your Superset instance and have the secret key available:

import jwt
import json
from datetime import datetime, timedelta
from flask import Flask, request, jsonify

app = Flask(__name__)

# Load your Superset configuration
SUPERSET_SECRET_KEY = "your-superset-secret-key-here"
SUPERSET_URL = "https://superset.yourcompany.com"
DASHBOARD_ID = 42  # The dashboard you want to embed

@app.route('/api/superset-token', methods=['POST'])
def generate_superset_token():
    """
    Generate a JWT token for embedding a Superset dashboard.
    """
    # Extract user context from your auth system
    user_id = request.user.id
    user_email = request.user.email
    user_role = request.user.role
    
    # Validate that this user can access this dashboard
    # (Your permission logic here)
    if not user_can_access_dashboard(user_id, DASHBOARD_ID):
        return jsonify({"error": "Unauthorized"}), 403
    
    # Construct the JWT payload
    # The 'sub' (subject) is typically the user ID
    # 'username' and 'email' are used by Superset for RLS and logging
    payload = {
        "sub": user_id,
        "username": user_email,
        "email": user_email,
        "iat": datetime.utcnow(),
        "exp": datetime.utcnow() + timedelta(hours=1),
    }
    
    # If you're using row-level security, add filters here
    # These will be applied to every query in the dashboard
    if user_role == "regional_manager":
        payload["filters"] = {
            "region": request.user.region
        }
    
    # Sign the token with your Superset secret key
    token = jwt.encode(payload, SUPERSET_SECRET_KEY, algorithm="HS256")
    
    return jsonify({"token": token})

def user_can_access_dashboard(user_id, dashboard_id):
    """
    Check if the user has permission to view this dashboard.
    This is your business logic—check your database, permission system, etc.
    """
    # Example: check if user is part of the right team
    return True  # Simplified for this example

A few critical details:

  • Expiration: Set a reasonable TTL (time-to-live) on the token. One hour is typical; adjust based on your security posture and user experience requirements. A shorter TTL means more frequent token refreshes; a longer TTL means more exposure if a token is compromised.
  • Secret key: The SUPERSET_SECRET_KEY must match the SUPERSET_SECRET_KEY configured in your Superset instance. If it doesn’t, token validation will fail.
  • Claims: The sub, username, and email claims are recognized by Superset. Other claims are ignored but can be useful for your own logging and analytics.
  • Row-level security: If you’re embedding dashboards with RLS enabled, the filters you add to the payload will be enforced by Superset. This is powerful—you can serve the same dashboard to different users with different data views.

Once your backend endpoint is working, test it with cURL or Postman. You should get back a valid JWT token.

Installing and Configuring the Embedded SDK

On the frontend, install the SDK via npm:

npm install @superset-ui/embedded-sdk

The SDK is a small, tree-shakeable package. It has no external dependencies beyond React (if you’re using React), so it won’t bloat your bundle.

Next, configure the SDK with your Superset URL. This is typically done in your app’s initialization code or in a context provider:

import { SupersetEmbeddedSDK } from '@superset-ui/embedded-sdk';

// Initialize the SDK with your Superset instance URL
SupersetEmbeddedSDK.configure({
  supersetUrl: 'https://superset.yourcompany.com',
});

That’s it. The SDK is now ready to use. The supersetUrl is the only required configuration—it tells the SDK where your Superset instance is running.

Embedding Your First Dashboard

Now for the fun part: rendering a dashboard inside your app. The SDK provides a React component, but it also works with vanilla JavaScript if you prefer.

Here’s a React example:

import React, { useState, useEffect } from 'react';
import { SupersetEmbeddedSDK } from '@superset-ui/embedded-sdk';

function EmbeddedDashboard({ dashboardId }) {
  const [token, setToken] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Fetch a token from your backend
    fetch('/api/superset-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
    })
      .then(res => res.json())
      .then(data => {
        setToken(data.token);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, []);

  if (loading) return <div>Loading dashboard...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <SupersetEmbeddedSDK.EmbeddedDashboard
      dashboardId={dashboardId}
      token={token}
      height="800px"
      width="100%"
    />
  );
}

export default EmbeddedDashboard;

The SupersetEmbeddedSDK.EmbeddedDashboard component is a wrapper around an iframe. It takes three essential props:

  • dashboardId: The numeric ID of the dashboard in Superset. You can find this in the Superset UI or in your database.
  • token: The JWT token generated by your backend.
  • height and width: CSS dimensions for the iframe.

Once the component mounts, the SDK will fetch the dashboard from Superset and render it inside the iframe. The user can interact with filters, drill into charts, and export data—all without leaving your app.

Implementing JWT Authentication and Security

JWT tokens are the security backbone of embedded Superset. Let’s dig deeper into how to implement them correctly.

First, ensure your Superset instance is configured to accept JWT tokens. In your Superset superset_config.py, set:

# Enable JWT authentication
AUTH_TYPE = AUTH_JWT
JWT_SECRET_KEY = "your-superset-secret-key-here"
JWT_ALGORITHM = "HS256"

The JWT_SECRET_KEY must match the key you use on your backend to sign tokens. If they don’t match, Superset will reject the token.

Next, think about token rotation and refresh. A one-hour token is reasonable, but if a user keeps the dashboard open for longer, they’ll get an authentication error when the token expires. You have a few options:

  1. Automatic refresh: Your frontend detects token expiration and fetches a new token from your backend without user interaction.
  2. Long-lived tokens: Issue tokens with a longer TTL (e.g., 8 hours), accepting the security trade-off.
  3. User-triggered refresh: Show a “Refresh” button that fetches a new token.

Here’s an automatic refresh pattern:

function EmbeddedDashboardWithRefresh({ dashboardId }) {
  const [token, setToken] = useState(null);
  const [tokenExpiry, setTokenExpiry] = useState(null);

  const fetchToken = async () => {
    const res = await fetch('/api/superset-token', { method: 'POST' });
    const data = await res.json();
    setToken(data.token);
    
    // Decode the token to extract the expiration time
    const decoded = jwt_decode(data.token);
    setTokenExpiry(decoded.exp * 1000); // Convert to milliseconds
  };

  useEffect(() => {
    fetchToken();
  }, []);

  // Refresh token 5 minutes before expiration
  useEffect(() => {
    if (!tokenExpiry) return;
    
    const now = Date.now();
    const timeUntilExpiry = tokenExpiry - now;
    const refreshTime = timeUntilExpiry - 5 * 60 * 1000; // 5 minutes before expiry
    
    if (refreshTime <= 0) {
      fetchToken();
      return;
    }
    
    const timer = setTimeout(() => fetchToken(), refreshTime);
    return () => clearTimeout(timer);
  }, [tokenExpiry]);

  if (!token) return <div>Loading...</div>;

  return (
    <SupersetEmbeddedSDK.EmbeddedDashboard
      dashboardId={dashboardId}
      token={token}
      height="800px"
      width="100%"
    />
  );
}

This approach refreshes the token 5 minutes before expiration, so the user never experiences a disruption.

For a deeper dive on token generation and Preset (a managed Superset offering), the Embed Your First Preset Dashboard in Minutes Using Flask | Tutorial covers similar patterns in a Flask context. And for more advanced embedding scenarios, Embedding Superset Dashboard into a Private Website with Authentication and RLS walks through integrating React with row-level security.

Theming and Styling Embedded Dashboards

By default, embedded dashboards inherit Superset’s default theme. But you probably want them to match your product’s branding. The SDK supports theme customization through CSS variables and configuration options.

The simplest approach is to override CSS variables. Superset uses CSS custom properties for colors, fonts, and spacing. You can inject a <style> tag into the iframe to customize the appearance:

function ThemedDashboard({ dashboardId }) {
  const [token, setToken] = useState(null);

  useEffect(() => {
    fetch('/api/superset-token', { method: 'POST' })
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);

  if (!token) return <div>Loading...</div>;

  return (
    <div style={{ '--primary-color': '#1f77b4', '--text-color': '#333' }}>
      <SupersetEmbeddedSDK.EmbeddedDashboard
        dashboardId={dashboardId}
        token={token}
        height="800px"
        width="100%"
        theme={{
          colors: {
            primary: '#1f77b4',
            secondary: '#ff7f0e',
          },
          typography: {
            fontFamily: 'Inter, sans-serif',
          },
        }}
      />
    </div>
  );
}

The theme prop accepts an object with colors, typography, and other style properties. This is passed to Superset, which applies them to the dashboard.

For more extensive theming, you can customize the Superset instance itself. In superset_config.py, you can set:

CUSTOM_THEME = {
    "colors": {
        "primary": "#1f77b4",
        "secondary": "#ff7f0e",
        "grayscale": ["#f5f5f5", "#e0e0e0", "#bdbdbd"],
    },
    "typography": {
        "fontFamily": "'Inter', sans-serif",
        "fontSize": 14,
    },
}

This applies the theme globally to all dashboards in your Superset instance, including embedded ones.

If you need pixel-perfect control, you can also inject custom CSS into the iframe using the SDK’s onLoad callback:

function CustomStyledDashboard({ dashboardId }) {
  const handleDashboardLoad = (iframeElement) => {
    const iframeDoc = iframeElement.contentDocument;
    if (iframeDoc) {
      const style = iframeDoc.createElement('style');
      style.textContent = `
        .dashboard-title {
          font-size: 24px;
          font-weight: bold;
          color: #1f77b4;
        }
        .filter-control {
          background-color: #f9f9f9;
          border-radius: 4px;
        }
      `;
      iframeDoc.head.appendChild(style);
    }
  };

  return (
    <SupersetEmbeddedSDK.EmbeddedDashboard
      dashboardId={dashboardId}
      token={token}
      onLoad={handleDashboardLoad}
      height="800px"
      width="100%"
    />
  );
}

For a comprehensive example of embedding with theming, Tackling on Embedding Apache Superset to your React app covers custom styling and component integration patterns.

Row-Level Security (RLS) and Dynamic Filters

One of the most powerful features of embedded Superset is row-level security. RLS allows you to serve the same dashboard to different users with different data views, all without creating separate dashboards.

RLS is implemented at the dataset level in Superset. You define a SQL clause that filters data based on user attributes. When a user views a dashboard, Superset automatically applies the RLS clause to every query.

For example, if you have a regional manager who should only see data for their region, you’d define an RLS clause like:

region = '{{ current_user_attribute("region") }}'

Then, in your JWT token, you include the user’s region:

payload = {
    "sub": user_id,
    "username": user_email,
    "email": user_email,
    "user_attributes": {
        "region": "North America"
    },
    "iat": datetime.utcnow(),
    "exp": datetime.utcnow() + timedelta(hours=1),
}

When the user views the dashboard, Superset will inject region = 'North America' into every query, automatically filtering the data.

You can include multiple attributes in the token:

payload = {
    "sub": user_id,
    "username": user_email,
    "email": user_email,
    "user_attributes": {
        "region": user.region,
        "department": user.department,
        "customer_id": user.customer_id,
    },
    "iat": datetime.utcnow(),
    "exp": datetime.utcnow() + timedelta(hours=1),
}

Then reference them in your RLS clauses:

region = '{{ current_user_attribute("region") }}'
AND department = '{{ current_user_attribute("department") }}'
AND customer_id = '{{ current_user_attribute("customer_id") }}'

For a detailed walkthrough on implementing RLS with embedded dashboards, Embedding Apache Superset Dashboards with Dynamic Row-Level Filters covers Docker setup, RLS configuration, and dynamic filter implementation.

Another practical approach using different backend stacks is demonstrated in Superset embedded dashboard setup: templ components, golang backend, which shows how to implement embedding with a Golang backend and custom templating.

Handling Filters and Interactive Features

Embedded dashboards are fully interactive. Users can apply filters, drill into charts, and export data. But sometimes you want to control which filters are available or pre-populate them with values from your app.

The SDK provides methods to interact with filters programmatically:

function DashboardWithFilters({ dashboardId, presetFilters }) {
  const dashboardRef = useRef(null);
  const [token, setToken] = useState(null);

  useEffect(() => {
    fetch('/api/superset-token', { method: 'POST' })
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);

  const handleDashboardReady = async () => {
    // Once the dashboard is loaded, apply preset filters
    if (dashboardRef.current && presetFilters) {
      dashboardRef.current.setFilters(presetFilters);
    }
  };

  if (!token) return <div>Loading...</div>;

  return (
    <SupersetEmbeddedSDK.EmbeddedDashboard
      ref={dashboardRef}
      dashboardId={dashboardId}
      token={token}
      onLoad={handleDashboardReady}
      height="800px"
      width="100%"
    />
  );
}

You can also listen to filter changes and react to them in your app:

const handleFilterChange = (filters) => {
  console.log('Filters changed:', filters);
  // Update your app's state, log analytics, etc.
};

return (
  <SupersetEmbeddedSDK.EmbeddedDashboard
    dashboardId={dashboardId}
    token={token}
    onFiltersChange={handleFilterChange}
    height="800px"
    width="100%"
  />
);

This pattern is useful for syncing the dashboard’s filters with other parts of your app, or for logging which filters users apply.

Advanced Configuration and Performance Tuning

As you scale embedded Superset, a few performance and reliability considerations become important.

Caching: Superset caches query results by default. When a user applies a filter, Superset checks the cache before re-running the query. You can control cache behavior via the Superset API or by setting cache TTLs in your configuration. For embedded dashboards that serve many users, good caching is critical to keeping query latency low.

Query timeout: Long-running queries can hang the dashboard. Set reasonable query timeouts in your Superset configuration:

SQLALCHEMY_QUERY_TIMEOUT = 30  # 30 seconds

If a query exceeds this timeout, Superset will cancel it and show an error. Adjust based on your data and user expectations.

Concurrent users: Each embedded dashboard is an iframe making requests to Superset. If you have many concurrent users, you need to ensure Superset can handle the load. Monitor your Superset instance’s CPU, memory, and database connections. Consider scaling Superset horizontally (multiple instances behind a load balancer) or vertically (more powerful hardware).

Network latency: The embedded SDK makes requests to Superset over the network. If your Superset instance is far from your users (e.g., in a different region), latency can accumulate. Consider deploying Superset closer to your users or using a CDN for static assets.

For a comprehensive look at embedding patterns and optimization, (Tutorial) Hosting Apache Superset part 2 - Embedding covers configuration, custom JavaScript, and performance considerations.

Monitoring and Debugging Embedded Dashboards

When embedded dashboards aren’t working as expected, you need good debugging tools. The SDK provides several mechanisms:

Browser DevTools: Open the browser console and look for errors. The SDK logs useful messages about token validation, dashboard loading, and filter changes.

Superset logs: Check your Superset instance’s logs for authentication errors, query failures, or configuration issues. If a token is invalid, Superset will log it.

Network tab: Inspect the network requests made by the iframe. You should see:

  1. A request to /api/v1/security/login with the JWT token.
  2. Requests to /api/v1/dashboard/{dashboardId} to fetch the dashboard definition.
  3. Requests to /api/v1/chart/data to fetch chart data.

If any of these fail, the error message will tell you what went wrong.

Custom logging: Add logging to your token generation endpoint to track which users are accessing which dashboards:

import logging

logger = logging.getLogger(__name__)

@app.route('/api/superset-token', methods=['POST'])
def generate_superset_token():
    user_id = request.user.id
    dashboard_id = request.json.get('dashboard_id')
    
    logger.info(f"Token requested for user {user_id}, dashboard {dashboard_id}")
    
    # ... rest of token generation ...
    
    logger.info(f"Token generated successfully for user {user_id}")
    return jsonify({"token": token})

This gives you an audit trail of who accessed what, which is valuable for security and troubleshooting.

Production Deployment Checklist

Before shipping embedded Superset to production, ensure you’ve addressed these items:

  • HTTPS: Your Superset instance and your app must both use HTTPS. Browsers block iframes that mix HTTP and HTTPS.
  • CORS: Configure CORS headers on your Superset instance to allow requests from your app’s domain.
  • Secret key rotation: Periodically rotate your SUPERSET_SECRET_KEY and JWT_SECRET_KEY. This is a multi-step process (generate new key, update config, re-sign tokens) but is important for security.
  • Token expiration: Ensure your token TTL is reasonable. One hour is a good default; adjust based on your security requirements.
  • Error handling: Implement graceful error handling in your embedded dashboard component. If token generation fails, show a helpful error message.
  • Monitoring: Set up alerts for token generation failures, Superset downtime, or unusual query latencies.
  • Documentation: Document the embedding setup for your team, including how to generate tokens, configure RLS, and troubleshoot issues.

Comparing Embedded Superset to Alternatives

You might be evaluating embedded Superset against Looker, Tableau, Power BI, or other BI platforms. Here’s how embedded Superset stacks up:

Cost: Embedded Superset (especially if you use D23, a managed offering) is significantly cheaper than Looker or Tableau. You’re paying for hosting and support, not per-user licensing. This is especially important if you’re embedding for external users (customers, partners) where per-user costs can explode.

Customization: Because Superset is open-source, you can customize nearly everything. Want to add a custom visualization? Write a React component. Want to modify the UI? Fork the repo. Looker and Tableau are more locked-down.

Flexibility: Superset’s API-first design makes it easy to integrate with your product. You can programmatically create dashboards, update data sources, and manage users. This is harder with Looker or Tableau.

Learning curve: Superset’s UI is simpler than Looker or Tableau, which can be a pro or con depending on your users’ sophistication.

For teams building embedded analytics into their products, especially at scale, embedded Superset is often the right choice. It’s powerful, flexible, and cost-effective.

Conclusion

Embedding Superset dashboards into your product is a straightforward process once you understand the token-based authentication model. Your backend generates a JWT token, your frontend passes it to the SDK, and the dashboard renders inside an iframe with full interactivity.

The patterns we’ve covered—token generation, JWT authentication, theming, RLS, and filter management—are the building blocks for production embedded analytics. Start with a simple embedded dashboard, add authentication and theming, then layer in RLS and dynamic filters as your needs grow.

If you’re looking for a managed Superset offering that handles infrastructure, scaling, and support, D23 is purpose-built for exactly this use case. Our team has embedded Superset at scale across data consulting engagements, and we’ve baked those patterns into our platform.

For more details on Superset’s embedding capabilities, the Embedded Superset SDK README is the definitive reference. And if you’re building a more complex embedding scenario, the resources we’ve linked throughout this guide—from Flask tutorials to React patterns to RLS implementations—cover the full spectrum of use cases.

Your engineering team now has everything needed to ship embedded analytics. The code is straightforward, the security model is sound, and the user experience is polished. Ship it with confidence.