Integration
Astro
Mounting the widget as a client:only React island
Astro's islands model fits the widget well: client:only skips server rendering entirely, which is exactly what the widget wants.
npx astro add react
npm install @superbridge/widget@astrojs/react has to be on a version that uses React 19 — the widget's peer range is >=19.
The stylesheet
Astro hoists and bundles styles imported from components, and the resulting order isn't something to rely on. Import both stylesheets as URLs instead and write the tags yourself, so the order is explicit:
---
// src/layouts/Layout.astro
import widgetStyles from "@superbridge/widget/styles.css?url";
import appStyles from "../styles/global.css?url";
---
<html lang="en">
<head>
<!-- Widget first: its theme variables are global, and yours should win. -->
<link rel="stylesheet" href={widgetStyles} />
<link rel="stylesheet" href={appStyles} />
</head>
<body>
<slot />
</body>
</html>The widget
Mount it as a client:only="react" island. That renders nothing on the server, which is what the widget needs — client:load would still prerender it and break.
---
// src/pages/bridge.astro
import Layout from "../layouts/Layout.astro";
import Bridge from "../components/Bridge";
---
<Layout>
<main class="mx-auto max-w-lg py-24">
<Bridge client:only="react" />
</main>
</Layout>Wrap the widget in your own component
Don't mount Widget directly from a .astro file. Island props are serialized to JSON, so function props can't cross the boundary — walletControl.evm.connect, onEvent and onFormChange would all be lost. Put them in a React component and mount that:
// src/components/Bridge.tsx
import { Widget } from "@superbridge/widget";
export default function Bridge() {
return (
<Widget
localDevelopmentApiKey={import.meta.env.PUBLIC_WIDGET_DEV_KEY}
onEvent={(event) => analytics.track(event)}
/>
);
}Serializable props — defaultForm, preferences — are fine to pass straight from .astro:
<Bridge defaultForm={{ amount: "100" }} client:only="react" />Environment variables
Astro exposes PUBLIC_-prefixed variables to client code via import.meta.env. As everywhere, keep the dev key to development only — put it in .env.development, or gate on import.meta.env.DEV.
One widget per page. Each island is an independent React root, so two client:only widgets on the same page would each mount their own copy — and the modal store is module-level, so they'd fight over what's open. Mount one island and put anything that needs to sit inside it — your own chrome — in that same React component.
Checklist
@astrojs/reacton React 19- Both stylesheets as explicit
<link>tags, widget first client:only="react", neverclient:load- A React wrapper component for any function or node props
- One island per page