Integration
TanStack Start
Head links, ClientOnly, and handing chains over from a loader
Start server-renders by default, so there are two things to get right: the stylesheet has to reach the SSR'd document head in the right order, and the widget itself has to stay off the server.
npm install @superbridge/widgetThe stylesheet
Resolve both stylesheets to URLs with ?url and order them in the root route's head. A bare side-effect import works in dev but doesn't put a <link> in the server-rendered HTML, so the first paint is unstyled.
// src/routes/__root.tsx
import { createRootRoute, HeadContent, Outlet, Scripts } from "@tanstack/react-router";
import widgetCss from "@superbridge/widget/styles.css?url";
import appCss from "../styles/app.css?url";
export const Route = createRootRoute({
head: () => ({
links: [
// Widget first: its theme variables are global, and yours should win.
{ rel: "stylesheet", href: widgetCss },
{ rel: "stylesheet", href: appCss },
],
}),
component: () => (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
),
});The widget
Wrap it in Start's ClientOnly. Give the fallback roughly the widget's dimensions so the page doesn't reflow when it mounts.
// src/routes/bridge.tsx
import { ClientOnly, createFileRoute } from "@tanstack/react-router";
import { Widget } from "@superbridge/widget";
export const Route = createFileRoute("/bridge")({
component: BridgePage,
});
function BridgePage() {
return (
<main className="mx-auto max-w-lg py-24">
<ClientOnly fallback={<div className="h-[560px] w-full" />}>
<Widget localDevelopmentApiKey={import.meta.env.VITE_WIDGET_DEV_KEY} />
</ClientOnly>
</main>
);
}If your app owns the wallet stack, put its providers inside the same ClientOnly boundary — wallet SDKs are browser-only too.
Environment variables
Start uses Vite, so client-visible variables are the VITE_-prefixed ones and they're inlined at build time. Gate the dev key with import.meta.env.DEV, or define it only in .env.development.
If your app has its own wallet stack
Deduplicate the shared libraries in your Vite config, or the widget won't see the provider you mounted:
// vite.config.ts
export default defineConfig({
resolve: {
dedupe: ["react", "react-dom", "wagmi", "viem", "@tanstack/react-query"],
},
});See Wallets.
ClientOnly renders the fallback on the server and on the first client render, so anything that needs a browser — your wallet SDK, and any of your own UI reading from it — belongs inside the same boundary.
Checklist
- Both stylesheets in
head.linksvia?url, widget first - Widget inside
ClientOnly, with a sized fallback - Wallet providers inside the same boundary
resolve.dedupeif you own the wallet stack