Peer dependencies
@tanstack/react-query only needs to be installed. The widget manages its own query client internally.
Tested frameworks
Server-side rendering
The widget server-renders. Render <Widget /> from a server-rendered route and the HTML contains a working widget, painted with its default state: the default pair for the configured chain, empty amounts, the default trade mode. The client resolves the tokens your input names shortly after mount, which shows up as a brief flash.
To remove that flash, resolve the tokens on the server with the @velora-dex/widget/ssr entry and pass the result back in through ssrState:
resolveWidgetSSRQueries takes it one step further and prefetches prices and bridge routes, so rates are on the page at first paint too.
Keep the /ssr entry out of browser bundles: FileCache imports node:fs, so value imports belong in server-only modules. Type imports are erased at compile time and are safe anywhere.
Call resolveWidgetInitState with the same config object you render <Widget /> with. It decides which token lists and trade modes are enabled, and the two renders disagree if it drifts.
The route has to render per request, since the resolved state depends on the request. A statically prerendered page cannot carry it.
Server-side rendering has the rest: prefetching rates with resolveWidgetSSRQueries, picking a cache backend for your runtime, acting on a token that could not be resolved, and deferring the widget behind a placeholder.
Client-only mount
Still supported, and the right answer when there is no server at request time (a static export) or you want the widget’s JavaScript off the critical path:
The initial HTML then has no widget in it, so anything that doesn’t run JavaScript sees only the placeholder.
Tailwind v4
The widget ships compiled Tailwind v4 styles scoped under the .velora-widget class. You don’t need Tailwind in your host app; the bundled styles are self-contained.
If your host app does use Tailwind, the widget’s scoping prevents its variables from leaking into your global stylesheet, and vice versa. See Customize.
Stylesheet auto-injection
The compiled stylesheet is auto-injected via vite-plugin-lib-inject-css when you import Widget. No explicit .css import is normally needed.
If your bundler strips side-effect imports aggressively (some custom Webpack / Rollup setups), force-include the stylesheet:
Error boundaries
Wrap the widget in an error boundary for production. A widget crash shouldn’t take down your whole page.
Memoize non-primitive config
Passing inline arrays or objects forces a re-render. Memoize them.
Lazy-load when offscreen
If the widget isn’t above the fold, defer it.
Bundle size
The widget is large: it ships wallet connectors, EVM tooling, and the full trading UI. Lazy-loading and route-based code splitting keep the initial bundle small.
Known caveats
- Strict-mode double-mount in dev triggers a brief double-fetch of price quotes, harmless and confined to development.
- Sandboxed iframes without
allow-popups and allow-storage-access-by-user-activation will break wallet-connection popups.
- Don’t server-render two widgets with different
input on one page. Each resolves its own state on the server, but they share one store in the browser, so the second adopts the first’s trade and React reports a hydration mismatch. Two widgets showing the same trade are fine.
- The
FileCache disk tier degrades quietly on a read-only filesystem: every write is best-effort, so you get no caching and no error. Serverless targets usually want Cache, or a backend of your own.
Related pages
Last modified on September 2, 2026