Superbridge
Start typing to search...
You need a Rollie to use the Superbridge Widget.

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/widget

The 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.links via ?url, widget first
  • Widget inside ClientOnly, with a sized fallback
  • Wallet providers inside the same boundary
  • resolve.dedupe if you own the wallet stack