Glossary term

Embedded Analytics

Embedded analytics are charts, dashboards, and reports rendered inside a SaaS product so customers can analyze their own data without leaving the app. They are the difference between a SaaS that holds data and a SaaS that helps customers act on it.

What embedded analytics is

Embedded analytics is the practice of rendering charts, dashboards, and reports inside another product’s UI rather than in a standalone BI tool. The end user logs into the host SaaS, navigates to a reports page, and sees their own data laid out as charts, tables, and exports — without ever opening Looker, Tableau, or Mode.

The point is end-user value. SaaS customers do not want to copy CSV exports into a separate analytics app. They want to see how their funnel, their customers, or their team is performing in the place they already do their work.

How it differs from internal analytics

Internal BI tools (Looker, Mode, Metabase) are for the company’s own analysts. Embedded analytics is for customers, and the constraints flip:

  • Tenancy isolation: every customer must see only their own data, every query.
  • Authentication: typically via signed JWTs that scope the query to the tenant.
  • Branding: the visualization must match the host product’s design system, not the BI vendor’s.
  • Performance: customers tolerate seconds in BI tools; they tolerate hundreds of milliseconds in their primary SaaS.

A good operator dashboard uses embedded analytics as one of its surfaces, typically for “show me the trend” questions. Customer-facing analytics tabs in B2B SaaS are pure embedded analytics products.

Build versus embed-via-vendor

The two production paths are:

  1. Embed a vendor. ThoughtSpot Embed, Looker Studio, Cube + Recharts, Sigma Embedded, Apache Superset Embed. Fastest to ship, hardest to customize.
  2. Build with a charting library. Recharts, ECharts, or D3, fed by your own query layer (often Cube, Hex, or a hand-rolled aggregation API).

The studio’s dashboard development team ships both depending on the engagement. Vendor embeds win when time-to-ship matters more than UI control. Custom builds win when the dashboard is a competitive feature of the SaaS, or when row-level security needs to be tightly integrated with the app’s RBAC model.

Security and tenancy patterns

The hardest part of embedded analytics is keeping tenant data isolated. The standard pattern uses three layers:

  • Signed JWTs at the embed boundary, carrying tenant id, user id, and scopes.
  • Row-level security in the warehouse or aggregation layer, enforced on every query.
  • Audit logging for every query, so suspicious cross-tenant access can be detected.

Other production decisions: cache aggregations behind a per-tenant key, use sandboxed iframes when embedding a vendor view, and version the embed token format so rotation is non-breaking.

Code-shape example

A minimal JWT-issuance + query-time scope check:

import jwt from "jsonwebtoken";

interface EmbedClaims {
  tenantId: string;
  userId: string;
  scopes: string[];          // e.g. ["dashboard.revenue.read"]
  exp: number;               // 30-minute TTL
}

// Issued at page-render time, baked into the iframe URL or postMessage.
export function issueEmbedToken(
  tenantId: string,
  userId: string,
  scopes: string[],
): string {
  return jwt.sign(
    {
      tenantId,
      userId,
      scopes,
      exp: Math.floor(Date.now() / 1000) + 30 * 60,
    } satisfies EmbedClaims,
    process.env.EMBED_JWT_SECRET!,
    { algorithm: "HS256" },
  );
}

// Verified on every query at the analytics service.
export function verifyEmbedToken(token: string): EmbedClaims {
  return jwt.verify(token, process.env.EMBED_JWT_SECRET!, {
    algorithms: ["HS256"],
  }) as EmbedClaims;
}

// Postgres row-level security, enabled per tenant.
// CREATE POLICY tenant_isolation ON revenue
// USING (tenant_id = current_setting('app.tenant_id')::uuid);

export async function runScopedQuery(
  claims: EmbedClaims,
  sql: string,
  params: unknown[],
): Promise<unknown[]> {
  return db.transaction(async (tx) => {
    await tx.execute(`SET LOCAL app.tenant_id = '${claims.tenantId}'`);
    return tx.query(sql, params);
  });
}

The combination of JWT scopes + Postgres RLS makes cross-tenant data leaks structurally impossible, even in the presence of a SQL-injection bug at the query layer.

How we implement it at Dashhold

Patterns we ship on embedded-analytics engagements:

  • Cube.js as the semantic layer when the team has a small data engineering bench. Cube handles caching, multi-tenancy, and SQL generation; the front-end uses Recharts for rendering.
  • Hand-rolled aggregation API when the team has Postgres expertise. Materialized views per common slice, refreshed on a schedule, served behind a JWT-gated REST or GraphQL surface.
  • ECharts for dense charts, Recharts for everyday charts. ECharts wins on data density and tooltip control; Recharts wins on React idiomatic-ness.
  • A small chart-spec DSL. Customers (and internal analysts) define a chart by spec, not code. The renderer lays it out. Lets non-engineers extend the dashboard without a deploy.
  • Per-tenant cache keys with explicit invalidation. Cache TTL plus a tenant_data_changed invalidation event. Stale dashboards damage customer trust faster than slow ones.

Common pitfalls

  • Skipping row-level security. Application-only filtering breaks the moment someone writes a SQL query without the right WHERE. RLS at the database is the structural backstop.
  • Long-lived embed tokens. Tokens older than 30 minutes leak into browser history, share buttons, and screenshots. Issue short, refresh transparently.
  • Letting customers customize chart SQL. They will ship queries that take down the warehouse. Expose a chart-spec DSL or a curated set of measures, never raw SQL.
  • Not caching aggregations. A dashboard that re-runs a 100M-row aggregation per page load is a customer-rage button.
  • Mixing internal and embedded analytics in one tool. The constraints flip. Internal tools accept seconds of latency; customer-facing does not. Internal tools tolerate vendor branding; customer-facing does not.
  • Forgetting the export path. Customers want CSV. Build it on day one, scoped by the same JWT, rate-limited, and logged.

Where embedded analytics fits

Every modern B2B SaaS — CRMs, payment platforms, logistics tools, marketing automation — eventually ships embedded analytics. Customers expect it the same way they expect filters and search. The studio’s dashboard development practice builds embedded-analytics surfaces as part of larger dashboard engagements, typically alongside operator-facing internal views and admin panels.

See also

Embedded Analytics FAQ

Common questions

What is embedded analytics in SaaS?
Embedded analytics is the practice of rendering charts and dashboards inside another product's UI rather than in a separate BI tool. The end user logs into the host SaaS, opens a 'Reports' page, and sees their own data laid out as charts, tables, and exports — without ever opening Looker or Tableau. It is one of the highest-leverage features a B2B SaaS can ship.
Should I build or buy embedded analytics?
Buy when time-to-ship matters more than UI control. Build when the dashboard is a competitive feature or when row-level security needs to be tightly integrated with your app's RBAC. Vendors like Sigma Embedded, ThoughtSpot Embed, and Cube + a charting library win on speed; custom builds with Recharts, ECharts, or D3 win on flexibility. Most platforms start with a vendor and migrate selectively over time.
How do I keep tenant data isolated in embedded analytics?
Three layers. Signed JWTs at the embed boundary, carrying tenant id and user scopes. Row-level security in the warehouse or aggregation layer, enforced on every query. Audit logging for every query, so suspicious cross-tenant access can be detected. Skipping any one of these is how data leaks happen.
What about real-time embedded analytics?
Sub-second freshness is feasible but rarely required. Most product analytics surfaces are fine with 5- to 15-minute latency, served from materialized aggregates. Real-time (sub-second) is needed for live operations dashboards or trading-style products. The trade-off is materially higher infrastructure cost — pick it deliberately.
Which charting library should I use for embedded analytics?
Recharts (React) and ECharts (vanilla JS) cover most use cases. D3 only when no library covers the visualization. Vega-Lite is a strong third option for declarative charts. Pick the one your team will actually maintain — chart libraries are long-term commitments, and switching is more work than it looks.

Let's build it together

Building something that depends on this?

The glossary is the short version. The custom analysis happens on the strategy call.