Why embedded analytics is the new table stakes for SaaS
Five years ago, shipping a CSV export button was enough to satisfy customer reporting needs. Three years ago, a read-only dashboard link got the job done. In 2028, your customers expect interactive analytics baked directly into your product, styled to match your brand, scoped to their data, and fast enough that they never notice it is powered by a separate system. Embedded analytics has moved from a nice differentiator to a retention requirement, and the tools available to build it have matured significantly.
The market has split into three distinct approaches. First, traditional BI tools that added embedding as a feature on top of their core dashboarding product. Metabase is the poster child here. Second, semantic layer platforms that sit between your data warehouse and any front end, exposing a governed API that your engineering team renders however it wants. Cube.js owns this category. Third, managed open-source BI platforms that offer embedding as a hosted service with less operational burden. Preset, the commercial host for Apache Superset, fills this role.
Each approach trades off differently on customization depth, engineering effort, pricing structure, and time to first dashboard. Picking the wrong one does not just cost you a migration later. It shapes your product roadmap, your data architecture, and how much of your engineering bandwidth gets consumed by analytics infrastructure instead of core features. This guide walks through each tool honestly, with real pricing, real architecture patterns, and real opinions on when each one fits.
Embedding approaches: iframe vs API vs SDK
The embedding method you choose determines everything downstream: how much control you have over the user experience, how you handle authentication, and how deeply the analytics feel like part of your product versus a bolted-on widget. The three primary approaches are iframe embedding, API-based rendering, and native SDK integration. Each tool in this comparison leans toward a different one.
Iframe embedding is the fastest path to something working. You generate a signed URL or JWT, drop an iframe into your app, and the BI tool renders inside it. Metabase's static embedding works exactly this way. You define a dashboard, lock the parameters (customer ID, date range, whatever scoping you need), sign the URL with your secret key, and embed it. The dashboard renders inside the iframe with Metabase's UI hidden. Setup takes an afternoon. The tradeoff is control. You are rendering someone else's HTML inside your page, which means theming is limited to what the tool exposes, interaction patterns are constrained, and the iframe boundary creates subtle UX friction like separate scrollbars, inconsistent loading states, and focus trapping issues that your front-end team will complain about.
API-based rendering is what Cube.js does natively. Cube does not render charts at all. It exposes a REST and GraphQL API (plus a SQL API) that returns structured data, and your front end renders it with whatever charting library you already use: Recharts, Chart.js, D3, Tremor, or something custom. This means total control over the visual output. Your embedded analytics look exactly like the rest of your product because they are built with the same component library. The tradeoff is engineering effort. You are building and maintaining the visualization layer yourself, which is real work, especially when customers start requesting new chart types, drill-downs, or ad-hoc exploration capabilities.
SDK embedding sits between the two. Metabase's interactive embedding SDK (released in 2026 and refined significantly since) gives you a React component library that renders Metabase questions and dashboards with deep theming hooks. You control colors, fonts, padding, and interaction behaviors through a configuration object rather than CSS overrides. Preset offers a similar React SDK for Superset-powered embeds. The SDK approach gives you more control than a raw iframe while requiring less engineering than building from raw API data. For most SaaS teams, this is the sweet spot.
The practical implication: if your product already has a sophisticated front-end component system and your engineering team has capacity, Cube.js gives you the most native-feeling result. If you want dashboards embedded quickly with reasonable customization, Metabase's SDK or Preset's SDK will get you there faster. If you just need a locked-down view of a dashboard with no interactivity, a signed iframe from any of the three works fine.
Multi-tenancy and row-level security
Multi-tenancy is the make-or-break requirement for SaaS embedded analytics. Every customer must see only their own data, every query must be scoped correctly, and a misconfiguration must not leak one customer's data to another. This is not a feature checkbox. It is a security boundary that has to be airtight, and the three tools handle it very differently.
Metabase offers two mechanisms. For static embedding, you pass tenant parameters (like a customer_id) into the signed JWT, and the dashboard filters are locked to those values. The customer never sees the filter controls, and the parameters cannot be tampered with because the JWT signature protects them. This is simple and secure for basic use cases. For interactive embedding, Metabase uses "sandboxing" on the Pro and Enterprise tiers, which applies row-level security rules to the underlying database connection. You define a sandboxing rule that says "when this user group queries this table, append WHERE customer_id = {{user_attribute}}" and every query gets scoped automatically. The sandboxing engine supports both simple attribute-based filtering and custom SQL expressions for complex cases. The limitation is that sandbox rules are defined per table, so if your schema has dozens of tables that all need tenant scoping, setup is tedious.
Cube.js handles multi-tenancy at the semantic layer, which is architecturally cleaner. You define security contexts in your Cube data model using JavaScript functions that receive the authenticated user's context and return filter conditions. A typical pattern looks like defining a dimension filter in your Cube schema that reads the tenant ID from the security context and applies it to every query automatically. Because Cube sits between the application and the warehouse, every downstream consumer (whether it is your custom React dashboard, a Metabase instance, or a Superset connection) gets the same tenant isolation without each tool needing its own security configuration. For SaaS products with multiple analytics surfaces, this single point of enforcement is a significant advantage.
Preset (Superset) supports row-level security through SQL-based rules applied to datasets. You write a SQL clause like "customer_id = '{{current_user_id}}'" and attach it to a role. When users with that role query the dataset, the clause gets appended to every query. This is powerful and flexible, but it requires careful management. The rules are defined in Superset's admin UI rather than in code, which means they are not version controlled by default and can drift if someone edits them manually. For teams that want code-managed security policies, Cube.js in front of Preset is the better pattern.
The architecture we recommend most often for serious SaaS embedding: put Cube.js in front of your warehouse as the security and semantic layer, then use either Metabase or Preset as the rendering layer on top. Cube handles tenant isolation, metric definitions, and caching. The BI tool handles visualization and user interaction. This separation of concerns means you can swap the rendering layer without rebuilding your security model, and your AI-powered analytics features can query through the same governed Cube API.
Customization, white-labeling, and design control
When analytics live inside your product, they need to look like your product. A dashboard that screams "this is Metabase" or "this is Superset" breaks the illusion and makes your product feel stitched together. White-labeling depth varies dramatically across these three tools.
Metabase has invested heavily in white-labeling on the Pro and Enterprise tiers. You can remove the Metabase logo, customize the color palette, set custom fonts, and configure the loading spinner. The interactive embedding SDK released in late 2026 lets you pass a theme object that controls background colors, card borders, text styles, and interactive states. For most SaaS products, this gets you 85% of the way to a seamless look. The remaining 15% involves CSS overrides for edge cases like tooltip styling, filter dropdown appearance, and the specific shade of grey on chart grid lines. It is workable but not pixel-perfect without effort.
Cube.js wins this category by default because it does not render anything. Since you build the visualization layer yourself, every pixel is yours to control. Your charts use your design tokens, your layout system, and your interaction patterns. There is no "remove the vendor logo" checkbox because there is no vendor UI. The cost is obvious: you are building and maintaining a charting front end, which is a non-trivial investment. Most teams using Cube pair it with a component library like Tremor, Recharts, or a commercial option like Highcharts, and build a reusable analytics component library that their product team can compose into different views.
Preset inherits Superset's theming system, which is more flexible than Metabase's but requires more technical knowledge to configure. Superset themes are defined via a CSS/LESS override system and a feature flag configuration. You can restyle nearly every element, but the documentation is sparse and the process involves trial and error. Preset's embedded analytics offering adds a cleaner theming API on top, with configurable color palettes and layout options. The guest token authentication flow supports passing custom CSS that gets injected into the embedded Superset instance. For teams with strong front-end engineers, Superset can be made to look like anything. For teams without that resource, it will look like Superset.
A practical note on design systems. If your SaaS product uses a design system like Material UI, Chakra, or Shadcn, the cleanest embedded analytics experience comes from Cube.js plus your own components built in that same system. If you are optimizing for speed over polish, Metabase's SDK with theme overrides gets you live faster. If you need the deepest visualization library and can invest in theming work, Preset gives you Superset's 40+ chart types with enough styling hooks to make them feel custom.
Pricing models and real cost at scale
Pricing is where embedded analytics decisions get painful, because the model that looks cheap at 10 customers can become ruinous at 1,000. The three tools use fundamentally different pricing structures, and understanding the scaling curve matters more than the starting price.
Metabase prices embedded analytics on the Pro tier at $500/month (self-hosted) or on the Cloud Pro plan at the same rate, covering up to 50 internal users. The critical detail for embedded use: customer-facing users who access your product's embedded dashboards do not count as Metabase users. They authenticate through your application, and Metabase sees them as anonymous consumers of signed embeds or SDK sessions. This means a SaaS product with 5,000 end users paying for embedded dashboards still pays Metabase's flat rate, not a per-seat fee. Enterprise tier pricing is custom and negotiated, typically landing between $1,500 and $4,000/month depending on your scale, with dedicated support and advanced security features. Compared to commercial embedded analytics vendors like Sisense ($50,000+/year) or Logi Analytics (similarly priced), Metabase is an order of magnitude cheaper.
Cube.js has a free self-hosted tier (Cube Core) that is genuinely production-ready for small deployments. Cube Cloud, the managed offering, starts at $99/month for the Premium tier, which includes Cube Store for pre-aggregations, auto-scaling, and monitoring. The Growth tier runs $299/month and adds multi-cluster deployment and higher query concurrency. Enterprise pricing is custom and typically starts around $1,500/month. The key distinction: Cube Cloud prices by compute usage and pre-aggregation storage, not by the number of end users querying through it. For a SaaS product serving thousands of tenants, this usage-based model can be significantly cheaper than per-seat alternatives. The caveat is that you are also paying for the front-end engineering time to build and maintain your visualization layer, which Cube does not provide.
Preset prices its embedded analytics offering starting at $20/viewer/month on the Professional tier, with a $500/month minimum. For larger deployments, the Enterprise Embedded tier uses flat pricing negotiated based on expected usage, typically starting around $2,000/month. The viewer-based pricing on the Professional tier is the trap to watch for. At 100 viewers, you are paying $2,000/month, which is reasonable. At 1,000 viewers, you are looking at $20,000/month, which starts to hurt. If your SaaS product has a large number of users who need analytics access, negotiate the flat Enterprise rate early or consider self-hosting Superset with your own embedding implementation.
The total cost comparison at 500 active analytics users: Metabase Pro self-hosted runs about $500/month for the license plus $80 to $200/month in infrastructure. Cube Cloud Growth plus your engineering time for the front end lands around $300 to $500/month in platform costs, plus whatever you value the ongoing front-end maintenance at (figure 10 to 20 hours/month of engineering time). Preset on the viewer model hits $10,000/month, or around $2,000 to $3,000/month if you negotiate the Enterprise flat rate. Self-hosted Superset with your own embedding code costs $400 to $800/month in infrastructure but requires dedicated engineering to maintain.
The cost math almost always favors Metabase or self-hosted Cube for high-volume embedded analytics. Preset wins when you need Superset's visualization depth and your user count is low enough that viewer pricing does not bite. For a deeper comparison of the underlying BI tools themselves, see our Metabase vs Superset vs Lightdash breakdown.
Performance, caching, and the semantic layer
Embedded analytics has a performance bar that internal BI tools do not. When a customer opens your product and sees a dashboard, it needs to load in under two seconds. Three seconds and they notice. Five seconds and they stop trusting the feature. Internal analysts will tolerate a slow dashboard because they know the warehouse is crunching. Your customers will not.
Cube.js was built around this problem. Its core architectural feature is the pre-aggregation layer, powered by Cube Store (a purpose-built Rust column store). You define pre-aggregations in your data model that specify how raw data should be rolled up: daily revenue by customer, weekly active users by plan tier, monthly feature usage by cohort. Cube materializes these rollups on a schedule or on demand, stores them in Cube Store, and routes queries to the pre-aggregated data when possible. The result is sub-100ms query response times for most dashboard queries, regardless of the underlying warehouse's speed. For embedded analytics where hundreds of tenants might hit the same dashboard pattern simultaneously, this caching layer is transformative. You stop paying for warehouse compute on repeated queries, and your customers get instant load times.
The semantic layer is Cube's other structural advantage. You define metrics, dimensions, joins, and access rules in a declarative data model (YAML or JavaScript). Every query, whether it comes from your React front end, a Metabase connection, or a direct API call, goes through the same governed definitions. This means "monthly recurring revenue" is calculated the same way everywhere, the join logic between your users table and your subscriptions table is defined once, and tenant security filters are applied uniformly. For SaaS products where data consistency matters (which is all of them), having a single semantic layer that every analytics surface reads from eliminates an entire class of bugs.
Metabase handles caching through its own query result cache, which stores serialized query results in the application database and serves them for subsequent requests within a configurable TTL. This works well for dashboards with moderate traffic. For high-concurrency embedded scenarios, Metabase's caching can struggle because each unique combination of parameters (customer ID, date range, filters) generates a separate cache entry. If you have 1,000 tenants each viewing a dashboard with 8 cards, that is 8,000 cache entries that need to stay warm. Metabase Pro adds model caching, which pre-computes and stores model results, helping with this pattern. For serious scale, putting Cube in front of Metabase and letting Cube handle the caching is the proven architecture.
Preset (Superset) uses Redis or Memcached for query result caching, with configurable TTLs per dataset. The Celery-based async query engine handles long-running queries gracefully, showing a progress indicator while the warehouse works. For embedded use cases, Preset's caching is adequate at moderate scale but does not have the pre-aggregation intelligence that Cube provides. Superset will cache the exact query result, but it will not automatically roll up data into faster-serving aggregates. At high concurrency, you are still hitting the warehouse for every unique query combination, just with a TTL buffer on repeated ones.
The architecture pattern we deploy most often: Cube.js as the semantic and caching layer connected to your warehouse, with pre-aggregations defined for every embedded dashboard query pattern. Then either Metabase or your own React components on top for rendering. Cube handles the hard performance problems (caching, pre-aggregation, concurrency), and the rendering layer just needs to display pre-computed results quickly. This pattern handles thousands of concurrent tenants without warehouse cost spiraling out of control.
SQL vs drag-and-drop and self-hosted vs managed
The query building experience matters differently depending on who is building the dashboards. For embedded analytics, the builder is usually your internal analytics team or your product engineers, not your customers. Your customers consume the finished output. This distinction changes what you should optimize for.
Metabase has the best drag-and-drop query builder of the three for non-technical users. Your product manager or data analyst can create a new question by clicking through tables, selecting columns, adding filters, and choosing aggregations without writing SQL. When they need more power, they can switch to the notebook editor (a structured query builder with join support) or drop into raw SQL. This three-tier progression is unique to Metabase and means your team can build embedded dashboards without dedicating a SQL-fluent engineer to every request. For SaaS companies where the analytics team is small, this flexibility matters enormously.
Cube.js does not have a drag-and-drop builder for end users. The query building happens in code. Your engineers define the data model, then build the queries in your application code or through Cube's Playground (a developer tool, not a user-facing builder). If you want to give your internal team a self-serve way to explore data and prototype dashboards, you typically pair Cube with a BI tool (Metabase, Superset, or even Metabase connected through Cube's SQL API) for the exploration phase, then translate the validated queries into your production front-end code.
Preset (Superset) offers SQL Lab, which is one of the best browser-based SQL editors available. Autocomplete, query history, saved queries, and async execution make it genuinely useful for daily work. The chart builder works from datasets (saved queries or table references) and provides a form-based interface for selecting metrics, dimensions, filters, and chart types. It is more complex than Metabase's builder but more powerful, supporting advanced features like time comparison, rolling calculations, and custom SQL expressions within the UI. For teams with SQL-comfortable analysts, Superset's builder is the most capable option.
On the self-hosted versus managed question, the decision framework is straightforward. Self-host if you have the engineering team to maintain it and your data residency or compliance requirements demand it. Go managed if your engineering bandwidth is better spent on your core product. Specific recommendations: Metabase self-hosted is easy to run and the open-source edition is free, making it the best self-hosted option for lean teams. Cube Cloud is worth the cost because managing Cube Store and pre-aggregation scheduling yourself is operationally complex. Preset is worth the cost if you want Superset but do not have a platform engineer to own the deployment. Self-hosted Superset is only a good idea if you already run Kubernetes and have someone comfortable with Celery, Redis, and Flask ops.
One hybrid pattern works well in practice: self-host Cube.js (it is a single Node process for small deployments) and use Metabase Cloud or Preset for the rendering layer. You get the cost savings of self-hosting the semantic layer while offloading the BI tool ops to a managed service. This combination keeps your monthly spend under $600 while serving thousands of embedded analytics users.
When to pick each tool and recommended architectures
After deploying all three across client products over the past two years, here is the decision framework we actually use. The right answer depends on three things: how much engineering capacity you can dedicate to analytics, how many tenants you are serving, and how deeply customized the analytics experience needs to be.
Pick Metabase Embedded if you want the fastest path to embedded dashboards that look good enough. You are a SaaS product with 50 to 5,000 tenants, your analytics team is small (one to three people), and you want them building dashboards with clicks rather than code. The Pro tier at $500/month is the best value in embedded analytics, period. Start with static signed embeds for v1, then upgrade to the interactive SDK when customers ask for drill-downs. Architecture: Metabase Pro (self-hosted or Cloud) connected directly to your warehouse, with JWT-based tenant scoping. Add Cube.js later if caching or semantic consistency becomes a bottleneck.
Pick Cube.js if your analytics are a core product feature, not a supporting one, and you have front-end engineers who can build and maintain a custom visualization layer. Cube is the right choice when you need total design control, when you are serving high-concurrency workloads across thousands of tenants, or when you need a single semantic layer governing analytics across multiple surfaces (product dashboards, customer reports, internal BI, AI features). Architecture: Cube Cloud (or self-hosted Cube) as the semantic and caching layer, your own React components using Recharts or Tremor for rendering, pre-aggregations defined for every embedded query pattern. Budget 10 to 20 hours/month of front-end engineering for ongoing visualization work.
Pick Preset if you need Superset's deep visualization library and SQL capabilities without the operational burden of self-hosting it. Preset makes sense when your analytics team is SQL-fluent, when you need chart types that Metabase does not support (geospatial, sankey, advanced pivot tables), and when your embedded user count is low enough that viewer pricing does not hurt. Architecture: Preset Enterprise Embedded with guest token authentication, row-level security for tenant isolation, and Redis-backed caching. Negotiate flat pricing early if you expect more than 200 embedded viewers.
The combination we deploy most often: Cube.js as the semantic layer and caching engine, connected to the data warehouse, with Metabase Pro on top for internal dashboard building and exploration, and custom React components for the customer-facing embedded experience. Cube handles security, caching, and metric definitions. Metabase gives your analytics team a self-serve tool to prototype and validate. Your product team builds the customer-facing views from Cube's API using your existing component library. This three-layer architecture handles scale gracefully, keeps costs predictable, and lets you swap any layer without rebuilding the others.
- Under 100 tenants, small analytics team: Metabase Pro with static embedding. Total cost under $600/month.
- 100 to 1,000 tenants, analytics as a product feature: Cube.js plus custom React front end. Total cost $300 to $800/month in platform, plus engineering time.
- Complex visualizations, SQL-heavy team, under 500 viewers: Preset Enterprise Embedded. Total cost $2,000 to $3,000/month negotiated flat.
- Enterprise scale, multiple analytics surfaces: Cube.js semantic layer plus Metabase for internal use plus custom front end for customers. Total cost $1,500 to $3,000/month in platform, plus dedicated analytics engineering.
The worst decision you can make is building a custom analytics system from scratch on top of raw SQL. We have seen SaaS teams spend six months building a bespoke dashboard framework that covers 20% of what Metabase gives you out of the box. Use the open-source and commercial tools that exist, compose them into the right architecture for your scale, and spend your engineering time on the features that differentiate your product.
If you are evaluating embedded analytics for your SaaS product and want a second opinion on the architecture, we help teams make exactly this decision. Book a free strategy call and we will walk through your tenant model, data stack, and product requirements to recommend the setup that fits your budget and growth trajectory.
Need help building this?
Our team has launched 50+ products for startups and ambitious brands. Let's talk about your project.