Creating a Custom Solana Wallet Connect UI with Next.js, Tailwind, and Shadcn
While solutions like the Solana Wallet Adapter provide a straightforward way to implement wallet connectivity, their customization options are often limited, leaving developers seeking more flexibility in their user interface designs. In this article, we delve into the realm of custom wallet connectors for Solana, exploring the @solana/wallet-adapter-react package and its associated UI library.
Setting Up the Next.js App with Tailwind CSS
1. Create a New Next.js Project:
Run the following command to initialize your Next.js application:
npx create-next-app@latest
After running this command, you’ll be prompted to set up various configurations such as TypeScript, ESLint, Tailwind CSS, and more.
2. Initial File Structure:
After the setup, your initial file structure should resemble something like this:
• .next/
• app/
• components/
• lib/
• node_modules/
• providers/
• public/
• .eslintrc.json
• .gitignore
• components.json
• next-env.d.ts
• next.config.mjs
• package-lock.json
• package.json
• postcss.config.js
• README.md
• tailwind.config.ts
• tsconfig.json
Setting Up Shadcn
Shadcn provides reusable components that are compatible with Tailwind CSS. It’s an open-source project that simplifies UI component integration.
1. Initialize Shadcn:
Run the following command:
npx shadcn-ui@latest init
2. Install Required NPM Libraries:
To create the wallet connector, install the following libraries:
npm i @solana/wallet-adapter-react @solana/wallet-adapter-react-ui @solana/web3.js @solana/wallet-adapter-wallets @solana/wallet-adapter-base
3. Add the Shadcn Dialog Component:
Run the following command to add the Dialog component:
npx shadcn-ui@latest add dialog
Coding the Wallet Connect Context Provider
Let’s create the wallet connect context provider and wrap the entire app with it.
"use client";
import { FC, ReactNode } from "react";
import {
ConnectionProvider,
WalletProvider,
} from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import "@solana/wallet-adapter-react-ui/styles.css";
import { clusterApiUrl } from "@solana/web3.js";
import {
PhantomWalletAdapter,
SolflareWalletAdapter,
MathWalletAdapter,
TrustWalletAdapter,
CoinbaseWalletAdapter,
} from "@solana/wallet-adapter-wallets";
import { useMemo } from "react";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { useWallet } from "@solana/wallet-adapter-react";
const WalletContextProvider: FC<{ children: ReactNode }> = ({ children }) => {
const network = WalletAdapterNetwork.Devnet;
// Initiate auto-connect
const { autoConnect } = useWallet();
// Provide a custom RPC endpoint
const endpoint = useMemo(() => clusterApiUrl(network), [network]);
// Wallets
const wallets = useMemo(
() => [
new PhantomWalletAdapter(),
new SolflareWalletAdapter(),
new MathWalletAdapter(),
new TrustWalletAdapter(),
new CoinbaseWalletAdapter(),
],
[network]
);
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>{children}</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
};
export default WalletContextProvider;
Explanation:
• Network Selection: The network is set to Devnet, which is a development network provided by Solana.
• Auto Connect: The useWallet hook from @solana/wallet-adapter-react is used to automatically connect to the wallet when the component is loaded.
• RPC Endpoint Setup: The clusterApiUrl function is used to get the RPC endpoint for the selected network, and this is memoized using React’s useMemo.
• Wallet Adapters Setup: An array of wallet adapters for different wallets is memoized.
• Context Providers: The context providers are set up in a hierarchy to provide the RPC connection context (ConnectionProvider), wallet context (WalletProvider), and wallet modal context (WalletModalProvider).
Wrapping the App with WalletContextProvider
Wrap the entire app using WalletContextProvider in layout.tsx:
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Header from "./components/Header";
import WalletContextProvider from "@/providers/WalletContextProvider";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<WalletContextProvider>
<Header />
{children}
</WalletContextProvider>
</body>
</html>
);
}
Creating a Header Component
Create a simple Header component to contain the wallet connect button: