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

Integration

React Router

React Router 7 and Remix, in framework and SPA mode

Applies to React Router 7 in framework mode and to Remix, which shares the same links / loader API.

npm install @superbridge/widget

The stylesheet

Import both stylesheets as URLs and return them from the root route's links export, widget first.

// app/root.tsx
import type { LinksFunction } from "react-router";

import widgetStyles from "@superbridge/widget/styles.css?url";
import appStyles from "./app.css?url";

export const links: LinksFunction = () => [
  // Widget first: its theme variables are global, and yours should win.
  { rel: "stylesheet", href: widgetStyles },
  { rel: "stylesheet", href: appStyles },
];

The widget

Framework mode server-renders, and there's no built-in client-only boundary, so gate it yourself. This is the whole thing:

// app/components/client-only.tsx
import { useEffect, useState, type ReactNode } from "react";

export function ClientOnly({
  children,
  fallback = null,
}: {
  children: () => ReactNode;
  fallback?: ReactNode;
}) {
  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);
  return <>{mounted ? children() : fallback}</>;
}

children is a function so the widget isn't even constructed during the server render. (If you already depend on remix-utils, its ClientOnly is the same component with the same signature.)

// app/routes/bridge.tsx
import { Widget } from "@superbridge/widget";

import { ClientOnly } from "../components/client-only";

export default 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>
  );
}

SPA mode

With ssr: false in react-router.config.ts there is no server render, so drop the gate and render <Widget /> directly — the Vite page is the shape you want.

Environment variables

React Router 7 and Remix both build with Vite, so client-visible variables are VITE_-prefixed and inlined at build time. Gate the dev key on import.meta.env.DEV or define it only in .env.development.

Don't route the dev key through a loader instead — that publishes it on every request, in production too.

If your app owns the wallet stack, add resolve.dedupe: ["react", "react-dom", "wagmi", "viem", "@tanstack/react-query"] to your Vite config so the widget sees the provider you mounted rather than a second copy of wagmi. See Wallets.

Checklist

  • Both stylesheets in the root links, widget first
  • A client-only boundary whose children is a function
  • Wallet providers inside the same boundary
  • Dev key gated to development