Skip to main content
A Next.js 14+ app-router integration that mounts the Velora Widget at /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

Restart next dev after creating or changing .env.local.

Run it

Visit http://localhost:3000/swap. You should see the widget render with Connect Wallet in the header.

First paint with the right tokens

A plain server render paints the widget’s default token pair, then swaps in the pair your input 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
Import values from @velora-dex/widget/ssr in server files only. FileCache pulls in node:fs, and a "use client" module that imports it breaks the client build. Type imports (import type { WidgetSSRState }) are erased at compile time and are safe anywhere.
Tokens are step one. resolveWidgetSSRQueries goes further and prefetches prices, rates and bridge routes into a dehydrated query cache, so the first paint carries live numbers too:
Budget for it: that serialized cache is usually the largest thing on the page, several hundred KB on a typical trade route.
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:
See Wallet management for the full pattern.
Last modified on September 2, 2026