Skip to main content
A Next.js 14+ app-router integration that mounts the Velora Widget at /swap. The widget reads window and localStorage on mount, so it must be client-only. We use next/dynamic with ssr: false.

File tree

my-app/
├─ package.json
├─ next.config.js
└─ src/app/
   ├─ layout.tsx
   ├─ page.tsx
   └─ swap/
      ├─ page.tsx
      └─ widget.tsx

Install

pnpm create next-app@latest my-app --typescript --app
cd my-app
pnpm add @velora-dex/widget @tanstack/react-query

src/app/layout.tsx

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

src/app/swap/widget.tsx

The widget itself, wrapped in dynamic(... { ssr: false }).
"use client";

import dynamic from "next/dynamic";

const Widget = dynamic(
  () => import("@velora-dex/widget").then((m) => ({ default: m.Widget })),
  { ssr: false, loading: () => <div>Loading widget…</div> }
);

export default function ClientWidget() {
  return (
    <Widget
      config={{
        theme: "light",
        partnerConfig: {
          partner: process.env.NEXT_PUBLIC_PARTNER_KEY,
        },
      }}
    />
  );
}

src/app/swap/page.tsx

import ClientWidget from "./widget";

export default function SwapPage() {
  return (
    <main style={{ display: "flex", justifyContent: "center", padding: "2rem" }}>
      <ClientWidget />
    </main>
  );
}

.env.local

NEXT_PUBLIC_PARTNER_KEY=my-app-name
Restart next dev after creating or changing .env.local.

Run it

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

Why ssr: false?

The widget reads window, localStorage, and prefers-color-scheme on mount. None of those exist during server rendering. dynamic(... { ssr: false }) defers the import to the browser, so the widget only ever runs client-side. If you forget ssr: false, you’ll see a hydration-mismatch error or a window is not defined exception.

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:
"use client";

import dynamic from "next/dynamic";
import { useConnection, useWalletClient } from "wagmi";

const Widget = dynamic(
  () => import("@velora-dex/widget").then((m) => ({ default: m.Widget })),
  { ssr: false }
);

export default function ClientWidget() {
  const { connector } = useConnection();
  const { data: walletClient } = useWalletClient();

  // Hand the EIP-1193 provider to the widget once a wallet is connected.
  const provider = walletClient?.transport;

  return (
    <Widget
      config={{
        widgetMode: "dapp",
        excludeUI: ["wallet-management"],
      }}
      provider={provider}
      events={{
        onConnectWalletClick: () => {
          // host-app wallet modal
          openConnectModal();
        },
      }}
    />
  );
}
See Wallet management for the full pattern.
Last modified on June 10, 2026