The @dub/embed-react package wraps an <iframe> hosted by RevRoute and exposes it as a single React component. It works in any React 18+ app — Next.js (App Router or Pages), Vite, Remix, CRA.
Install the package
pnpm add @dub/embed-reactPeer dependencies: react@^18 and react-dom@^18 (React 19 is also supported).
Mint a token on your backend
The component needs a short-lived embed token. Generate it from a route that knows which partner the current user corresponds to — never expose the workspace API key to the browser.
import { NextResponse } from "next/server";
export async function POST() {
// Replace with your real auth + partner lookup
const partnerId = await getPartnerIdForCurrentUser();
if (!partnerId) return new NextResponse("Not a partner", { status: 403 });
const res = await fetch(
"https://api.revroute.ru/tokens/embed/referrals",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.REVROUTE_API_KEY!}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ partnerId, programId: process.env.REVROUTE_PROGRAM_ID }),
},
);
if (!res.ok) return new NextResponse(await res.text(), { status: 500 });
const { publicToken, expires } = await res.json();
return NextResponse.json({ publicToken, expires });
}The response:
{
"publicToken": "dub_embed_eyJhbGc...",
"expires": "2026-05-15T13:34:56.789Z"
}publicToken is safe to send to the browser. It is valid only for the referenced partner and expires at the expires timestamp.
Mount the component
"use client";
import { DubEmbed } from "@dub/embed-react";
import { useEffect, useState } from "react";
export default function ReferPage() {
const [token, setToken] = useState<string | null>(null);
useEffect(() => {
fetch("/api/embed/token", { method: "POST" })
.then((r) => r.json())
.then(({ publicToken }) => setToken(publicToken));
}, []);
if (!token) return <div>Loading…</div>;
return (
<DubEmbed
data="referrals"
token={token}
style={{ width: "100%", height: "100vh", border: 0 }}
/>
);
}That’s the whole integration — the partner’s referral dashboard is now embedded in your app.
React (Vite, CRA, etc.)
The component is identical in non-Next.js projects. Only the token-minting endpoint changes — implement it in your own server (Express, Fastify, Rails, Django, whatever you use).
import { DubEmbed } from "@dub/embed-react";
export function ReferralTab({ token }: { token: string }) {
return (
<DubEmbed
data="referrals"
token={token}
style={{ width: "100%", height: "100%", border: 0 }}
/>
);
}Token refresh
Embed tokens expire after ~1 hour. If a user keeps the dashboard open longer, refetch the token before it expires and pass the new value to <DubEmbed token={...} />. Changing the token prop transparently re-authenticates the iframe without losing state.
A minimal hook:
function useEmbedToken() {
const [token, setToken] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
async function refresh() {
const r = await fetch("/api/embed/token", { method: "POST" });
const { publicToken, expires } = await r.json();
if (cancelled) return;
setToken(publicToken);
const ms = Math.max(60_000, new Date(expires).getTime() - Date.now() - 60_000);
setTimeout(refresh, ms);
}
refresh();
return () => { cancelled = true; };
}, []);
return token;
}Content Security Policy
If your app sends a CSP header, allow the RevRoute embed origin:
Content-Security-Policy:
frame-src https://app.revroute.ru;
connect-src 'self' https://api.revroute.ru;Next steps
- Referral dashboard — props and customization
- Theming — match the embed to your brand
- API authentication — manage the workspace API key used to mint tokens