Skip to content

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.

Terminal window
npm install @digisquares/datasquares-embed-react
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.

The ref handle sends typed commands over the versioned ds:* postMessage protocol:

embed.current?.setParams({ region: 'east' }) // wins over the URL's p_* defaults
embed.current?.setPage('page-id')
embed.current?.refresh()
const { pageId, params } = await embed.current!.getState()
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 server
import { 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.

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.

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 pins strict-origin-when-cross-origin on 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.

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.

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.