diff --git a/README.md b/README.md
index f7bcead..7921a01 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,59 @@ const { liquidity, refreshLiquidity } = useBridgeLiquidity({
`BridgeCompare` uses this data to prioritize high-liquidity routes and warn on low-liquidity paths.
+## Next.js SSR Compatibility
+
+BridgeWise UI components now support server-side rendering (SSR) with Next.js App Router and Pages Router.
+
+### Basic Usage
+
+```tsx
+import { BridgeStatus, ClientOnly } from '@bridgewise/ui-components';
+
+// Safe for SSR - renders skeleton during server-side render
+export default function BridgePage() {
+ return (
+ Loading bridge...}>
+
+
+ );
+}
+```
+
+### Next.js Dynamic Import
+
+For maximum compatibility, use the Next.js adapter:
+
+```tsx
+import { BridgeStatusDynamic, BridgeCompareDynamic } from '@bridgewise/next-adapter';
+
+export default function BridgePage() {
+ return (
+
+
+
+
+ );
+}
+```
+
+### SSR Utilities
+
+Use built-in utilities for browser-only code:
+
+```tsx
+import { useIsClient, safeStorage, createBrowserGuard } from '@bridgewise/ui-components';
+
+function MyComponent() {
+ const isClient = useIsClient();
+
+ if (!isClient) return Server rendering...
;
+
+ const stored = safeStorage.get('user-prefs', '{}');
+ return Client ready: {stored}
;
+}
+```
+
## Project setup
```bash
diff --git a/libs/ui-components/package.json b/libs/ui-components/package.json
index ebefc07..4cbf2c1 100644
--- a/libs/ui-components/package.json
+++ b/libs/ui-components/package.json
@@ -9,6 +9,7 @@
".": "./src/index.ts",
"./theme": "./src/theme/index.ts",
"./headless": "./src/components/headless/index.ts",
+ "./ssr": "./src/ssr-utils/index.ts",
"./styles/globals.css": "./src/styles/globals.css"
},
"peerDependencies": {
diff --git a/libs/ui-components/src/components/SSR/ClientOnly.tsx b/libs/ui-components/src/components/SSR/ClientOnly.tsx
new file mode 100644
index 0000000..f8ae5e9
--- /dev/null
+++ b/libs/ui-components/src/components/SSR/ClientOnly.tsx
@@ -0,0 +1,20 @@
+'use client';
+
+import { type ReactNode } from 'react';
+import { useIsClient } from '../../ssr-hooks/useIsClient';
+
+export interface ClientOnlyProps {
+ /** Content rendered only after client-side hydration. */
+ children: ReactNode;
+ /** Optional fallback rendered during SSR / before hydration. */
+ fallback?: ReactNode;
+}
+
+/**
+ * Renders its children only on the client.
+ * During SSR (and the first client render) the `fallback` is shown instead.
+ */
+export function ClientOnly({ children, fallback = null }: ClientOnlyProps) {
+ const isClient = useIsClient();
+ return <>{isClient ? children : fallback}>;
+}
diff --git a/libs/ui-components/src/index.ts b/libs/ui-components/src/index.ts
index 999277b..68d5679 100644
--- a/libs/ui-components/src/index.ts
+++ b/libs/ui-components/src/index.ts
@@ -118,3 +118,16 @@ export type {
WalletProviderProps,
WalletContextValue,
} from './wallet';
+
+// SSR Compatibility Utils
+export { isServer, isClient } from './ssr-utils/env';
+export { safeStorage } from './ssr-utils/safe-storage';
+export { createBrowserGuard, ServerAccessError } from './ssr-utils/browser-guard';
+
+// SSR Hooks
+export { useIsClient } from './ssr-hooks/useIsClient';
+export { useIsomorphicLayoutEffect } from './ssr-hooks/useIsomorphicLayoutEffect';
+
+// SSR Components
+export { ClientOnly } from './components/SSR/ClientOnly';
+export type { ClientOnlyProps } from './components/SSR/ClientOnly';
diff --git a/libs/ui-components/src/ssr-hooks/index.ts b/libs/ui-components/src/ssr-hooks/index.ts
new file mode 100644
index 0000000..116256e
--- /dev/null
+++ b/libs/ui-components/src/ssr-hooks/index.ts
@@ -0,0 +1,2 @@
+export * from './useIsClient';
+export * from './useIsomorphicLayoutEffect';
\ No newline at end of file
diff --git a/libs/ui-components/src/ssr-hooks/useIsClient.ts b/libs/ui-components/src/ssr-hooks/useIsClient.ts
new file mode 100644
index 0000000..750ab89
--- /dev/null
+++ b/libs/ui-components/src/ssr-hooks/useIsClient.ts
@@ -0,0 +1,17 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+
+/**
+ * Returns `true` once the component has mounted on the client.
+ * Useful for guarding browser-only code inside components.
+ */
+export function useIsClient(): boolean {
+ const [isClient, setIsClient] = useState(false);
+
+ useEffect(() => {
+ setIsClient(true);
+ }, []);
+
+ return isClient;
+}
diff --git a/libs/ui-components/src/ssr-hooks/useIsomorphicLayoutEffect.ts b/libs/ui-components/src/ssr-hooks/useIsomorphicLayoutEffect.ts
new file mode 100644
index 0000000..f4e00d5
--- /dev/null
+++ b/libs/ui-components/src/ssr-hooks/useIsomorphicLayoutEffect.ts
@@ -0,0 +1,12 @@
+'use client';
+
+import { useEffect, useLayoutEffect } from 'react';
+import { isServer } from '../utils/env';
+
+/**
+ * `useLayoutEffect` on the client, `useEffect` on the server.
+ * Avoids the React SSR warning about useLayoutEffect.
+ */
+export const useIsomorphicLayoutEffect = isServer
+ ? useEffect
+ : useLayoutEffect;
diff --git a/libs/ui-components/src/ssr-utils/browser-guard.ts b/libs/ui-components/src/ssr-utils/browser-guard.ts
new file mode 100644
index 0000000..4f4be01
--- /dev/null
+++ b/libs/ui-components/src/ssr-utils/browser-guard.ts
@@ -0,0 +1,41 @@
+import { isServer } from './env';
+
+/**
+ * Error thrown when a browser-only API is accessed on the server.
+ */
+export class ServerAccessError extends Error {
+ constructor(api: string) {
+ super(
+ `[BridgeWise] "${api}" is not available during server-side rendering. ` +
+ `Wrap the call in a useEffect or use the component.`,
+ );
+ this.name = 'ServerAccessError';
+ }
+}
+
+/**
+ * Creates a proxy that throws `ServerAccessError` when any property is
+ * accessed on the server, and delegates to `factory()` on the client.
+ *
+ * @example
+ * ```ts
+ * const ethereum = createBrowserGuard('window.ethereum', () => window.ethereum);
+ * ```
+ */
+export function createBrowserGuard(
+ name: string,
+ factory: () => T,
+): T {
+ if (!isServer) {
+ return factory();
+ }
+
+ return new Proxy({} as T, {
+ get(_target, prop) {
+ // Allow Symbol.toPrimitive / toJSON etc. to avoid noisy errors in SSR
+ // serialisation paths.
+ if (typeof prop === 'symbol') return undefined;
+ throw new ServerAccessError(`${name}.${String(prop)}`);
+ },
+ });
+}
diff --git a/libs/ui-components/src/ssr-utils/env.ts b/libs/ui-components/src/ssr-utils/env.ts
new file mode 100644
index 0000000..48a4409
--- /dev/null
+++ b/libs/ui-components/src/ssr-utils/env.ts
@@ -0,0 +1,9 @@
+/**
+ * Environment detection utilities for SSR compatibility.
+ */
+
+/** Returns `true` when running on the server (no `window` global). */
+export const isServer = typeof window === 'undefined';
+
+/** Returns `true` when running in a browser environment. */
+export const isClient = !isServer;
diff --git a/libs/ui-components/src/ssr-utils/index.ts b/libs/ui-components/src/ssr-utils/index.ts
new file mode 100644
index 0000000..a63d329
--- /dev/null
+++ b/libs/ui-components/src/ssr-utils/index.ts
@@ -0,0 +1,3 @@
+export * from './env';
+export * from './safe-storage';
+export * from './browser-guard';
\ No newline at end of file
diff --git a/libs/ui-components/src/ssr-utils/safe-storage.ts b/libs/ui-components/src/ssr-utils/safe-storage.ts
new file mode 100644
index 0000000..c679cbb
--- /dev/null
+++ b/libs/ui-components/src/ssr-utils/safe-storage.ts
@@ -0,0 +1,34 @@
+import { isServer } from './env';
+
+/**
+ * A storage wrapper that is safe to use in SSR environments.
+ * All operations silently no-op on the server.
+ */
+export const safeStorage = {
+ get(key: string): string | null {
+ if (isServer) return null;
+ try {
+ return window.localStorage.getItem(key);
+ } catch {
+ return null;
+ }
+ },
+
+ set(key: string, value: string): void {
+ if (isServer) return;
+ try {
+ window.localStorage.setItem(key, value);
+ } catch {
+ // Storage may be full or blocked (e.g. private browsing).
+ }
+ },
+
+ remove(key: string): void {
+ if (isServer) return;
+ try {
+ window.localStorage.removeItem(key);
+ } catch {
+ // Silently ignore.
+ }
+ },
+};
diff --git a/packages/next-adapter/dynamic.tsx b/packages/next-adapter/dynamic.tsx
new file mode 100644
index 0000000..cefc6e5
--- /dev/null
+++ b/packages/next-adapter/dynamic.tsx
@@ -0,0 +1,27 @@
+'use client';
+
+import dynamic from 'next/dynamic';
+import type { ClientOnlyProps } from '@bridgewise/ui-components';
+import type { BridgeStatusProps as BridgeWidgetProps } from '@bridgewise/ui-components';
+
+/**
+ * Dynamically imported `BridgeWidget` with SSR disabled.
+ * Drop-in replacement for App Router and Pages Router.
+ */
+export const BridgeStatusDynamic = dynamic(
+ () => import('@bridgewise/ui-components').then((m) => m.BridgeStatus),
+ { ssr: false },
+);
+
+export const BridgeCompareDynamic = dynamic(
+ () => import('@bridgewise/ui-components').then((m) => m.BridgeCompare),
+ { ssr: false },
+);
+
+/**
+ * Dynamically imported `ClientOnly` with SSR disabled.
+ */
+export const ClientOnlyDynamic = dynamic(
+ () => import('@bridgewise/ui-components').then((m) => m.ClientOnly),
+ { ssr: false },
+);
diff --git a/packages/next-adapter/index.ts b/packages/next-adapter/index.ts
new file mode 100644
index 0000000..a82ca9c
--- /dev/null
+++ b/packages/next-adapter/index.ts
@@ -0,0 +1,5 @@
+// Re-export everything from the core UI components for convenience.
+export * from '@bridgewise/ui-components';
+
+// Next.js-specific dynamic imports (SSR disabled).
+export { BridgeStatusDynamic, BridgeCompareDynamic, ClientOnlyDynamic } from './dynamic';
diff --git a/packages/next-adapter/package.json b/packages/next-adapter/package.json
new file mode 100644
index 0000000..436d069
--- /dev/null
+++ b/packages/next-adapter/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "@bridgewise/next-adapter",
+ "version": "0.1.0",
+ "description": "Next.js adapter for BridgeWise SDK with SSR support",
+ "main": "src/index.ts",
+ "exports": {
+ ".": "./src/index.ts",
+ "./dynamic": "./src/dynamic.tsx"
+ },
+ "peerDependencies": {
+ "@bridgewise/ui-components": "^0.1.0",
+ "next": ">=13.4.0",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "devDependencies": {
+ "typescript": "^5.0.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0"
+ },
+ "keywords": [
+ "bridgewise",
+ "nextjs",
+ "ssr",
+ "react",
+ "defi",
+ "bridge"
+ ]
+}
\ No newline at end of file