Install & Embed
Four ways onto the page — React, mount(), a script tag, or Next.js — plus the compatibility floor.
npm i @glacislabs/stablegenie-zerodeltareact and react-dom (>= 18) are peer dependencies for the main entry — they are kept external in the ESM and CJS builds, so your React is reused and nothing is double-bundled. The package itself has zero runtime dependencies.
CSS is auto-injected. There is no stylesheet to import. The widget writes its own styles into the document on first render, scoped to its own root.
You also need the keyless proxy. The widget's default
apiBaseis a same-origin endpoint you deploy, which injects the ZeroDelta key server-side. Without it every request answers503 proxy_misconfigured. This is not optional — see The Keyless Proxy.
Entry points
@glacislabs/stablegenie-zerodelta
the React components, mount, the theme helpers, createEngine
yes
…/core
the framework-agnostic engine — quoting, routing, encoding, the pre-sign validator, the on-chain verifier
no
…/mount
imperative mounting for non-React hosts
yes (bundled by your build)
…/standalone
the prebuilt IIFE with React bundled in, for plain <script> embeds
no
Use …/core if you want the routing and validation engine without pulling in React — it is verifiably React-free.
React
import { StableGenieExchange } from "@glacislabs/stablegenie-zerodelta";
// `provider`: your app's EIP-1193 provider (wagmi connector, window.ethereum…) so the user does not connect twice.
<StableGenieExchange
variant="wide"
defaultFromChainId={42161}
defaultFromAsset="USDT"
defaultToChainId={8453}
defaultToAsset="USDC"
provider={provider}
/>An exact-receive deposit instead:
Imperative mount()
For React hosts that do not write JSX at the embed point.
variantmeans two different things, and this is the sharpest footgun in the package. Inmount()it selects the component —"pay"(default),"swap","exchange"or"rwa". In a component it selects the layout —"compact","wide"or"pill".mount()strips its own selector before rendering, so it cannot express a layout: there is no way to get apillorwidecard throughmount(). Use JSX, or the script-tag helpers below, which pass the display variant through.
Plain <script>
No React on the host at all. The standalone build bundles its own.
VERSION is the exact release you pin. The current one is on the package's npm page, and the playground's Script tag tab prints this line with it already filled in.
The global exposes five helpers, each taking the same props as the matching React component — including the display variant — and returning an unmount function:
mountPay(el, props)
StableGeniePay
mountSwap(el, props)
StableGenieSwap
mountExchange(el, props)
StableGenieExchange
mountRwaExchange(el, props)
StableGenieExchange with product: "rwa"
mountExpress(el, props)
PaymentIsland — ZeroDelta Link
jsDelivr works too: https://cdn.jsdelivr.net/npm/@glacislabs/stablegenie-zerodelta@VERSION/dist/standalone.global.js
Pin an exact version, and prefer self-hosting if your CSP disallows third-party script origins — this widget sits in a payment path. The file ships inside the package at
dist/standalone.global.js, so you can copy it into your own static assets at build time:
Next.js and server rendering
The widget is a client component. Importing it on the server does not crash — style injection, localStorage and wallet discovery are all guarded on typeof window — but it renders nothing useful there.
The package ships no
"use client"directive. Wrap it in your own client component:
Or skip SSR entirely where you would rather not ship it in the server bundle:
Route the proxy as a Next handler. Mount it at
app/api/widget/v1/[...path]/route.tsso the widget's defaultapiBaseresolves without configuration. The proxy logic is plain data in and plain data out — see The Keyless Proxy for a complete handler.
Compatibility
React
18 and 19 (peer dependency)
Module formats
ESM + CJS, with .d.ts and .d.cts types
Bundlers
Vite, webpack, Next.js, Rollup, esbuild — nothing special required
Node
>= 20, for building only
Runtime dependencies
none
Licence
MIT
Browser APIs used: fetch, BigInt, URL / URLSearchParams, Promise.all, CustomEvent, and localStorage (guarded — it degrades cleanly in private or sandboxed contexts). Any browser with those needs no polyfill; there is no viem or ethers to shim because neither is used.
When something does not render
Every request answers 503 proxy_misconfigured
The proxy has no STABLEGENIE_API_KEY set
403 origin_not_allowed
Cross-origin embed with the origin missing from WIDGET_ALLOWED_ORIGINS
Card renders but no chains or tokens load
The /api/widget/v1/* route is not mounted or not rewritten to your handler
Direct settlement never appears as a route
Your proxy does not allow screen/wallet — the engine cannot screen, so it fails closed. See The Keyless Proxy
A payment succeeds on-chain but onSuccess never fires
Your CSP connect-src does not allow the verification RPC — see The Keyless Proxy
variant: "pill" is ignored
You used mount(), which cannot express a display variant
Try a configuration first
The widget playground renders every component against the production gateway and prints the exact React, mount() and <script> snippet for whatever you configure. The snippets on this page are the same ones it generates.

