React embed SDK
@digisquares/datasquares-embed-react wraps the embed iframe in a typed React
component: auto-resize, events for clicks and filter changes, host-side
controls for parameters and pages, and a server-side helper for minting
row-scoped tokens.
npm install @digisquares/datasquares-embed-reactDrop in the component
Section titled “Drop in the component”import { useRef } from 'react'import { DataSquaresEmbed, type DataSquaresEmbedHandle } from '@digisquares/datasquares-embed-react'
function Analytics({ token }: { token: string }) { const embed = useRef<DataSquaresEmbedHandle>(null)
return ( <DataSquaresEmbed ref={embed} baseUrl="https://app.datasquares.ai" // your DataSquares app URL contentId="your-dashboard-id" token={token} theme="dark" params={{ region: 'west' }} // p_* defaults baked into the URL onReady={({ pages }) => console.log('pages', pages)} onPointClick={({ cardId, value }) => console.log('clicked', cardId, value)} onFilterChange={(filters) => console.log('filters', filters)} /> )}The iframe grows to fit its content. Fix the height instead with
autoResize={false} and a height.
Control it from your app
Section titled “Control it from your app”The ref handle sends typed commands over the versioned ds:* postMessage
protocol:
embed.current?.setParams({ region: 'east' }) // wins over the URL's p_* defaultsembed.current?.setPage('page-id')embed.current?.refresh()const { pageId, params } = await embed.current!.getState()Events
Section titled “Events”| Prop | Fires when |
|---|---|
onReady |
the embed authorized and rendered (contentType, pages) |
onError |
it could not render — invalid, forbidden, network, password-required |
onPageChange |
the visible dashboard page changed |
onPointClick |
a chart data point was clicked |
onFilterChange |
the cross-filter set changed (chart click, slicer, clear) |
Mint tokens on your server — never in the browser
Section titled “Mint tokens on your server — never in the browser”Token minting calls POST /api/embed-tokens with your DataSquares
credentials, so the exchange belongs on your backend. Your endpoint
authenticates your user, chooses that user’s row-level-security context, and
returns only the token:
// e.g. an Express/Next route handler on YOUR serverimport { createEmbedToken } from '@digisquares/datasquares-embed-react'
const { token } = await createEmbedToken({ apiUrl: 'https://api.datasquares.ai', accessToken: process.env.DATASQUARES_TOKEN!, contentType: 'dashboard', contentId: 'your-dashboard-id', rlsContext: { tenant_id: currentUser.tenantId }, // per-viewer row scoping expiresAt: new Date(Date.now() + 3600_000).toISOString(),})The rlsContext is bound into the token server-side — the viewer cannot see
or change it, and every query the embed runs is scoped by it.
Ask-AI in an embed
Section titled “Ask-AI in an embed”The floating Ask AI panel is a property of the share, not the
component: if the publisher turned it on for that share (Share dialog →
Link/Embed tab → Ask-AI for viewers), it renders inside the embedded page
automatically — there’s no prop to enable or disable it from your app, and no
event fires for it beyond the ordinary iframe lifecycle. It shows the same
way whether the host uses <DataSquaresEmbed> or a plain iframe.
The agent only ever answers from that one share’s own cards, is rate-limited per share, and is off by default. See Ask-AI on a share for what it does and its limits.
Restrict which sites may embed
Section titled “Restrict which sites may embed”In Sharing → Embed → Embed settings, list the domains allowed to render
your embeds (acme.com, *.partner.io). The embed identifies its host by the
frame’s ancestor origin and the DataSquares API refuses hosts not on the list.
An empty list allows every site; your own share links always keep working.
Two things to know as the host page:
- Don’t wrap the embed in
referrerpolicy="no-referrer"— an unidentifiable host is refused when a restriction is configured. The component pinsstrict-origin-when-cross-originon its iframe, which sends the origin and nothing more. - The restriction controls where content can be rendered. The token is still the credential — treat it like one.
White-label
Section titled “White-label”The same Embed settings panel carries a logo, custom CSS, and toolbar
defaults applied inside every embed (snippet URL parameters win when both are
set). Remote url() and @import are refused in the CSS — reference data:
URIs instead. Embed usage (views, chart clicks, filter changes, page switches)
rolls up per share in the same panel.
Plain iframe
Section titled “Plain iframe”No React? The Share dialog’s Embed tab produces a copy-paste iframe snippet — same tokens, same allowlist, same theming, without the events and controls the SDK adds.