/swap. The widget server-renders, so no dynamic(... { ssr: false }) wrapper is needed: mark the file "use client" and render it. Next renders it to HTML on the request and hydrates it in the browser.
Out of the box that first paint shows the widget’s default state. To make it show the tokens the request asks for, resolve them on the server first — see First paint with the right tokens.
File tree
Install
src/app/layout.tsx
src/app/swap/widget.tsx
The widget itself. "use client" because it uses hooks and browser APIs once it mounts; it is still rendered to HTML on the server for the initial request.
src/app/swap/page.tsx
.env.local
next dev after creating or changing .env.local.
Run it
First paint with the right tokens
A plain server render paints the widget’s default token pair, then swaps in the pair yourinput names once token lists load in the browser. That flash is what the server-only @velora-dex/widget/ssr entry removes: resolve the tokens on the server, pass them to the widget as ssrState, and hand the same object to the client so both renders agree.
Four files. The first matters more than it looks: resolveWidgetInitState must run with the same config you render with, so export it once.
src/lib/widget-config.ts
src/lib/widget-ssr.ts
src/app/swap/page.tsx
src/app/swap/widget.tsx
resolveWidgetSSRQueries goes further and prefetches prices, rates and bridge routes into a dehydrated query cache, so the first paint carries live numbers too:
Server-side rendering is the framework-agnostic version of this, and covers what comes next: prefetching rates, cache backends for serverless runtimes, telling a token that isn’t listed from a list that failed to load, and deferring the widget behind a placeholder.
Do you still need ssr: false?
Not for correctness. The widget renders on the server, and ssrState is what keeps the server and client renders in agreement.
You would still reach for it when there is no server at request time, since a statically exported page cannot resolve anything per request. Some teams also use it to keep the widget’s JavaScript off the critical path, though in the App Router a Suspense boundary usually serves that better: the widget arrives later in the same response instead of waiting for a second round trip.
Either way the initial HTML has no widget in it, so crawlers and link unfurlers that don’t run JavaScript see only the placeholder, and the visitor watches the default tokens resolve after mount.
WidgetSkeleton is a static placeholder shipped from the main entry. It reserves the widget’s shape, so the swap-in shifts nothing on the page.
With dApp-mode wallet
If your app already manages the wallet (wagmi, RainbowKit, or any other host-side wallet library), switch to dApp mode and pass the provider:Related pages
- Install — package install and basic setup.
- Wallet management — standalone vs dApp mode.
- Server-side rendering — the full reference for
@velora-dex/widget/ssr. - Compatibility — peer dependencies, tested frameworks, known caveats.
- React example — same widget on a Vite stack.