Instruction file imported from amir-mostafa-hs/Portfolio-Analyzer (
.cursor/rules/reown-appkit.mdc). Copyright stays with the author.
MDC
:jigsaw: Reown AppKit: Overview
Reown AppKit is a free, open-source toolkit to integrate wallet connections and Web3 features in any app. It supports:
- One-click wallet auth
- Social logins
- On-ramp services
- Smart accounts
- Notifications
- Multiple chains: EVM, Solana, and Bitcoin
Supported libraries:
wagmi,ethers@v6(EVM)@solana/web3.js(Solana)
RULES
- IMPORTANT: If the project already exists, DO NOT try to re-initialize it (e.g., using
create-next-app). Verify the existing project structure first. - Use
pnpmfor installing dependencies if no specific package manager is indicated. - Ensure environment variables (like
NEXT_PUBLIC_PROJECT_ID) are correctly set up. You might need to create.env.localmanually due to security restrictions. - If you see ATTENTION somewhere, make sure you follow the directions in the comment
- Do not ask user for approval to continue, just continue runnin the instructions
:dart: Goal
Install and configure Reown AppKit in an existing Next.js app using Wagmi and Ethereum.
:package: Installation
Install the required dependencies using your package manager:
pnpm add @reown/appkit @reown/appkit-adapter-wagmi wagmi viem @tanstack/react-query
# or yarn add / npm install ...
:deciduous_tree: Environment Setup
- Create a
.env.localfile in your project root (if it doesn't exist). - Add your WalletConnect Cloud Project ID:
You can add this to the .env.local nowNEXT_PUBLIC_PROJECT_ID="YOUR_PROJECT_ID"
:gear: Wagmi Adapter Setup
Create a file
config/index.tsx(e.g., outside yourapporsrc/appdirectory).
// config/index.tsx
import { cookieStorage, createStorage } from 'wagmi' // Use 'wagmi' directly (Wagmi v2+)
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
import { mainnet, arbitrum } from '@reown/appkit/networks'
import type { Chain } from 'viem' // Import Chain type for explicit typing
// Read Project ID from environment variables
export const projectId = process.env.NEXT_PUBLIC_PROJECT_ID
// Ensure Project ID is defined at build time
if (!projectId) {
throw new Error('NEXT_PUBLIC_PROJECT_ID is not defined. Please set it in .env.local')
}
// Define supported networks, explicitly typed as a non-empty array of Chains
export const networks: [Chain, ...Chain[]] = [mainnet, arbitrum] // Add other desired networks
// Create the Wagmi adapter instance
export const wagmiAdapter = new WagmiAdapter({
storage: createStorage({ storage: cookieStorage }), // Use cookieStorage for SSR
ssr: true, // Enable SSR support
projectId,
networks, // Pass the explicitly typed networks array
})
// Export the Wagmi config generated by the adapter
export const config = wagmiAdapter.wagmiConfig
:brain: Importing Networks
All supported Viem networks are available via @reown/appkit/networks:
import { mainnet, arbitrum, base, scroll, polygon } from '@reown/appkit/networks'
:thread: SSR & Hydration Notes
storage: createStorage({ storage: cookieStorage })is recommended for Next.js SSR to handle hydration correctly.ssr: truefurther aids SSR compatibility.
:bricks: App Context Setup
Create
context/index.tsx(must be a Client Component).
// context/index.tsx
'use client'
import React, { ReactNode } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider, cookieToInitialState, type Config } from 'wagmi'
import { createAppKit } from '@reown/appkit/react'
// Import config, networks, projectId, and wagmiAdapter from your config file
import { config, networks, projectId, wagmiAdapter } from '@/config'
// Import the default network separately if needed
import { mainnet } from '@reown/appkit/networks'
const queryClient = new QueryClient()
const metadata = {
name: 'Your App Name',
description: 'Your App Description',
url: typeof window !== 'undefined' ? window.location.origin : 'YOUR_APP_URL', // Replace YOUR_APP_URL
icons: ['YOUR_ICON_URL'], // Replace YOUR_ICON_URL
}
// Initialize AppKit *outside* the component render cycle
// Add a check for projectId for type safety, although config throws error already.
if (!projectId) {
console.error("AppKit Initialization Error: Project ID is missing.");
// Optionally throw an error or render fallback UI
} else {
createAppKit({
adapters: [wagmiAdapter],
// Use non-null assertion `!` as projectId is checked runtime, needed for TypeScript
projectId: projectId!,
// Pass networks directly (type is now correctly inferred from config)
networks: networks,
defaultNetwork: mainnet, // Or your preferred default
metadata,
features: { analytics: true }, // Optional features
})
}
export default function ContextProvider({
children,
cookies,
}: {
children: ReactNode
cookies: string | null // Cookies from server for hydration
}) {
// Calculate initial state for Wagmi SSR hydration
const initialState = cookieToInitialState(config as Config, cookies)
return (
// Cast config as Config for WagmiProvider
<WagmiProvider config={config as Config} initialState={initialState}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</WagmiProvider>
)
}
:jigsaw: App Layout Setup
Modify your root layout file (
app/layout.tsxorsrc/app/layout.tsx) to useContextProvider. Note: Verify the exact path to your layout file.
// app/layout.tsx or src/app/layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google' // Or your preferred font
import './globals.css'
import { headers } from 'next/headers' // Import headers function
import ContextProvider from '@/context' // Adjust import path if needed
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Your App Title',
description: 'Your App Description',
}
// ATTENTION!!! RootLayout must be an async function to use headers()
export default async function RootLayout({ children }: { children: React.ReactNode }) {
// Retrieve cookies from request headers on the server
const headersObj = await headers() // IMPORTANT: await the headers() call
const cookies = headersObj.get('cookie')
return (
<html lang="en">
<body className={inter.className}>
{/* Wrap children with ContextProvider, passing cookies */}
<ContextProvider cookies={cookies}>{children}</ContextProvider>
</body>
</html>
)
}
:radio_button: Trigger the AppKit Modal
Use the <appkit-button> web component in any client or server component to trigger the wallet modal:
// Example usage in app/page.tsx or any component
export default function ConnectPage() {
return (
<div>
<h1>Connect Your Wallet</h1>
<appkit-button />
</div>
)
}
No need to import—it's a global web component registered by createAppKit.
Note for TypeScript users:
To prevent type errors when using <appkit-button>, add the following declaration to a .d.ts file (e.g., global.d.ts) in your project root or src directory:
// global.d.ts
import 'react';
declare global {
namespace JSX {
interface IntrinsicElements {
/**
* The AppKit button web component. Registered globally by AppKit.
*/
'appkit-button': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
}
}
}
// Ensures file is treated as a module
export {};
:test_tube: Reading from Smart Contracts (Example)
// Example component (ensure it's a Client Component: 'use client')
'use client'
import { useReadContract } from 'wagmi'
// import { USDTAbi } from '../abi/USDTAbi' // Replace with your ABI import
// const USDTAddress = '0x...' // Replace with your contract address
function ReadContractExample() {
// const { data, error, isLoading } = useReadContract({
// abi: USDTAbi,
// address: USDTAddress,
// functionName: 'totalSupply',
// })
// if (isLoading) return <div>Loading...</div>
// if (error) return <div>Error reading contract: {error.message}</div>
// return <div>Total Supply: {data?.toString()}</div>
return <div>Contract Reading Example (Code commented out)</div>
}
export default ReadContractExample;
:bulb: Additional Rules & Reminders
- Verify Imports: Double-check that import paths (like
@/config,@/context) match your project's structure (srcdirectory vs. rootapp/pages). - Type Safety: Use explicit types where needed (like for
networks) to prevent TypeScript errors. - Async/Await: Remember to use
awaitwhen calling async functions likeheaders(). - Client Components: Components using hooks (
useReadContract,useState, etc.) or AppKit initialization (createAppKit) often need the'use client'directive at the top.