This is Part 2 of our guide to building a Cardano transaction application with Next.js and Weld. In this section, you'll implement wallet connectivity with proper SSR support.
Weld is a universal Cardano wallet connector library that simplifies wallet integration by providing:
A unified interface for multiple Cardano wallets
TypeScript support with full type safety
React hooks for state management
Server-side rendering compatibility
See the for more information.
Implementation Steps
1. Create the Weld Provider Component
First, we need to create a provider component that will make Weld available throughout your application. This component will handle wallet connectivity state and server/client hydration:
The updateInterval option (set to 30 seconds) helps maintain active wallet connections during longer user sessions.
2. Integrate the Provider in Your Layout
Next, we'll update the app layout to use our ClientWeldProvider. Notice that we include the lastConnectedWallet prop to restore wallet connection state between server and client. This is important for maintaining wallet connectivity during page navigation, and preventing React hydration errors due to server-side rendering being inconsistent with client-side rendering.
// src/app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
// Add the three imports.
import { ClientWeldProvider } from "@/components/WeldProvider";
import { cookies } from "next/headers";
import { STORAGE_KEYS } from "@ada-anvil/weld/server";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Cardano Transaction App",
description: "Send Cardano transactions using the Anvil API",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
// Add the cookie retrieval logic.
const cookieStore = await cookies();
const wallet = cookieStore.get(STORAGE_KEYS.connectedWallet)?.value;
const changeAddress = cookieStore.get(STORAGE_KEYS.connectedChange)?.value;
const stakeAddress = cookieStore.get(STORAGE_KEYS.connectedStake)?.value;
const lastConnectedWallet = wallet ? { wallet, changeAddress, stakeAddress } : undefined;
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{/* Wrap the children with the ClientWeldProvider component. */}
<ClientWeldProvider lastConnectedWallet={lastConnectedWallet}>
{children}
</ClientWeldProvider>
</body>
</html>
);
}
3. Understanding SSR with Weld
Our implementation addresses three critical aspects of wallet integration in Next.js with SSR:
Hydration Consistency: By retrieving wallet connection data from cookies during server-side rendering, we ensure the initial client-side state matches what was rendered on the server, preventing React hydration errors.
Connection Persistence: The updateInterval configuration keeps an active wallet connection stable through periodic state checks.
Seamless Reconnection: The tryToReconnectTo property provides a smooth user experience by maintaining wallet connections across page refreshes.
4. Create a Wallet Connector Component
Now let's create a UI component that users will interact with to connect their wallets:
// src/components/WalletConnector.tsx
"use client";
import { useState } from "react";
// Import Weld hooks to get wallet and user extensions.
import { useWallet, useExtensions } from "@ada-anvil/weld/react";
// Import supported wallets from Weld.
import { SUPPORTED_WALLETS } from "@ada-anvil/weld";
// Helper function to truncate address for display
const truncateAddress = (address: string) => {
if (!address) return "";
return `${address.slice(0, 8)}...${address.slice(-8)}`;
};
// Component to display wallet info
const WalletInfo = ({ label, value }: { label: string; value: string }) => (
<div>
<span>{label} </span>
<span>
<b>{value}</b>
</span>
</div>
);
export default function WalletConnector() {
const wallet = useWallet();
const { supportedMap: installedWallets, isLoading } = useExtensions(
"supportedMap",
"isLoading",
);
const availableWallets = SUPPORTED_WALLETS.filter((w) =>
installedWallets.has(w.key),
);
const [selectedWallet, setSelectedWallet] = useState<string>("");
// Reset connection state if wallet selection changes
const handleWalletSelection = (value: string) => {
if (wallet.isConnectingTo && value !== wallet.isConnectingTo) {
// Cancel any pending connection
wallet
.disconnect()
.catch((err) => console.error("Failed to disconnect wallet:", err));
}
setSelectedWallet(value);
};
const handleConnect = async (walletKey?: string) => {
if (!walletKey) return;
try {
await wallet.connectAsync(walletKey);
} catch (error) {
console.error("Failed to connect wallet:", error);
}
};
return (
<section className="paper">
<h2>Wallet</h2>
{wallet.isConnected ? (
// Connected state - show wallet info and disconnect button
<>
<WalletInfo label="Connected to:" value={wallet.displayName || ""} />
<WalletInfo
label="Address:"
value={truncateAddress(wallet.changeAddressBech32 || "")}
/>
<WalletInfo
label="Balance:"
value={`${wallet.balanceAda?.toFixed(2) || "0.00"} ADA`}
/>
<button
onClick={() =>
wallet
.disconnect()
.catch((err) =>
console.error("Failed to disconnect wallet:", err),
)
}
className="btn mt-4"
>
Disconnect
</button>
</>
) : // Disconnected state - show wallet selector and connect button
isLoading ? (
<div>Detecting wallet extensions...</div>
) : (
<div>
<select
className="custom-rounded mb-4"
name="wallet-key"
value={selectedWallet}
onChange={(e) => handleWalletSelection(e.target.value)}
>
{availableWallets.length === 0 ? (
<option value="">No wallets</option>
) : (
<>
<option value="">Select a wallet</option>
{availableWallets.map((w) => (
<option key={w.key} value={w.key}>
{w.displayName}
</option>
))}
</>
)}
</select>
<button
onClick={() => selectedWallet && handleConnect(selectedWallet)}
className="btn text-center"
disabled={wallet.isConnecting || availableWallets.length === 0}
>
{wallet.isConnecting
? `Connecting to ${wallet.isConnectingTo}...`
: selectedWallet
? "Connect Wallet"
: "Select a Wallet"}
</button>
</div>
)}
</section>
);
}
5. Update the Home Page
Now that we have created the component. Lets update your home page to include the wallet connector component:
Now let's test the wallet integration to ensure it's working correctly:
Start your development server:
npm run dev
Navigate to your application (usually at http://localhost:3000)
Test the wallet connection flow:
Verify that available wallets are correctly detected in the dropdown
Select a wallet and click "Connect Wallet"
Confirm that the wallet popup appears requesting connection
After approving, verify that wallet information is displayed:
Connected wallet name
Truncated wallet address
ADA balance
Test disconnection:
Click the "Disconnect" button
Verify that the UI returns to the wallet selection state
If wallet connection fails, check your browser console for errors. Common issues include:
Wallet extension not properly installed
Wallet needs to be enabled for dApp interactions.
Wallet locked (needs to be unlocked first)
Incompatible wallet versions
Troubleshooting
No Wallets Detected
If no wallets are appearing in the dropdown list:
Make sure you have wallet extensions installed (Eternl, Lace, etc.)
Refresh the page after installing a new wallet extension
Check your browser console for any errors
What's Next?
Congratulations! You've completed Part 2 of the guide. Your application can now detect wallets, connect to them, and display wallet information.
See the for more information.
Now that you have a working wallet integration, you're ready to implement transaction functionality. In , we'll create the components and API routes needed to build and submit Cardano transactions.