A comprehensive Next.js template for building a cryptocurrency portfolio viewer on the Monad blockchain. This template demonstrates how to integrate Moralis API for token data, Reown AppKit for wallet connectivity, and provides a modern, responsive UI for displaying wallet balances.
- Features
- Tech Stack
- Project Structure
- Getting Started
- Architecture
- Component Documentation
- API Documentation
- Customization
- Troubleshooting
- Deployment
- π Wallet Connection: Connect wallets using Reown AppKit (formerly WalletConnect)
- π Address Search: Search and view portfolio for any Monad address without connecting a wallet
- π° Token Balances: View all ERC20 token balances with real-time data
- π΅ Price Data: Display USD prices and portfolio values
- π Network Toggle: Switch between Monad Testnet and Mainnet
- π± Responsive Design: Mobile-first, modern UI built with Tailwind CSS
- β‘ Performance: Optimized with React Query caching and Next.js optimizations
- π¨ Custom Fonts: Beautiful typography with Google Fonts integration
- π Type Safe: Full TypeScript support for better DX
- Framework: Next.js 16 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS 4
- Wallet: Reown AppKit + Wagmi + Viem
- Data Fetching: TanStack React Query
- API: Moralis Web3 Data API
- UI Components: Custom components (Button, Card, etc.)
- Fonts: Google Fonts (Montserrat, Orbitron, Anonymous Pro, etc.)
monad-portfolio-viewer-using-moralis-api/
βββ src/
β βββ app/
β β βββ api/
β β β βββ wallet/
β β β βββ balances/
β β β βββ route.ts # API route for fetching token balances
β β βββ globals.css # Global styles and CSS variables
β β βββ layout.tsx # Root layout with providers
β β βββ page.tsx # Main page component
β βββ components/
β β βββ layout/
β β β βββ Header.tsx # App header with navigation
β β βββ portfolio/
β β β βββ PortfolioDashboard.tsx # Main dashboard component
β β β βββ TokenList.tsx # Token list container
β β β βββ tokens/
β β β βββ TokenRow.tsx # Individual token row component
β β βββ ui/
β β β βββ Button.tsx # Reusable button component
β β β βββ Card.tsx # Card component
β β β βββ ConnectIcon.tsx # Wallet connection icon
β β β βββ NetworkToggle.tsx # Network switcher component
β β β βββ SearchBar.tsx # Address search component
β β βββ wallet/
β β βββ ConnectButton.tsx # Wallet connect/disconnect button
β βββ context/
β β βββ index.tsx # Wallet context provider
β β βββ NetworkContext.tsx # Network state management
β βββ hooks/
β β βββ useTokenBalances.ts # Custom hook for token data
β βββ lib/
β β βββ utils.ts # Utility functions (cn helper)
β βββ config.ts # Wagmi adapter configuration
βββ .env.local # Environment variables (create this)
βββ package.json
βββ tsconfig.json
βββ README.md
- Node.js 18+ installed
- npm, yarn, pnpm, or bun package manager
- Moralis API Key (Get it here)
- Reown Project ID (Get it here)
- Clone or use this template
# If cloning
git clone <repository-url>
cd monad-portfolio-viewer-using-moralis-api
# Or use as template- Install dependencies
npm install
# or
pnpm install
# or
yarn install- Set up environment variables
Create a .env.local file in the root directory:
# Moralis API Key (server-side only)
MORALIS_API_KEY=your_moralis_api_key_here
# Reown Project ID (client-side accessible)
NEXT_PUBLIC_PROJECT_ID=your_reown_project_id_hereGetting Your Credentials:
-
Moralis API Key:
- Sign up at Moralis Dashboard
- Navigate to Settings β API Keys
- Copy your API Key
-
Reown Project ID:
- Sign up at Reown Cloud
- Create a new project
- Copy your Project ID
- Run the development server
npm run dev
# or
pnpm dev
# or
yarn devOpen http://localhost:3000 in your browser.
User Action β Component β Hook β API Route β Moralis API
β
React Query Cache
β
Component Update
- Server-Side API Route: Protects your Moralis API key by handling requests server-side
- React Query: Manages data fetching, caching, and state synchronization
- Context Providers: Manage wallet connection and network state globally
- Custom Hooks: Encapsulate data fetching logic for reusability
The main context provider that sets up wallet connectivity:
// Wraps the app with:
- WagmiProvider: Provides Wagmi configuration
- QueryClientProvider: Provides React Query client
- NetworkProvider: Manages network stateKey Features:
- Initializes Reown AppKit modal
- Configures Monad network support
- Provides wallet connection context
Manages network selection (Testnet/Mainnet):
const { network, setNetwork, chainId } = useNetwork();
// network: "testnet" | "mainnet"
// chainId: "143" (testnet) | "monad" (mainnet)Features:
- Persists network selection in localStorage
- Provides chain ID for API calls
- Automatically updates when network changes
Main dashboard component that orchestrates the portfolio view:
// Handles:
- Wallet connection state
- Address search functionality
- Token balance fetching
- Loading and error states
- Network-aware data fetching
- Switching between connected wallet and searched addressFeatures:
- Search bar for viewing any address portfolio
- Works without wallet connection
- Shows current address being viewed
- Quick switch between searched address and connected wallet
Props: None (uses hooks internally)
Displays the list of tokens with loading/error states:
<TokenList
tokens={Token[]}
isLoading={boolean}
error={string | undefined}
showLowValueTokens={boolean}
/>Features:
- Loading skeleton
- Error display with helpful messages
- Empty state handling
- Token filtering (low value tokens)
Individual token display component:
<TokenRow token={Token} />Displays:
- Token logo (with fallback)
- Token name and symbol
- USD price
- Token amount
- USD value
Toggle button for switching between networks:
<NetworkToggle />Features:
- Visual indication of active network
- Smooth transitions
- Persists selection
Address search component for viewing any wallet's portfolio:
<SearchBar
onSearch={(address: string) => void}
placeholder?: string
/>Features:
- Ethereum address validation (0x + 40 hex characters)
- Search on Enter key or button click
- Clear button (X icon)
- Error messages for invalid addresses
- Works independently of wallet connection
Props:
onSearch: Callback function called with validated addressplaceholder: Optional placeholder text (default: "Search wallet address...")
Custom wallet connection button:
<ConnectButton />States:
- Disconnected: Shows "Connect Wallet" button
- Connected: Shows address with link to explorer + disconnect button
Fetches token balances for a wallet address.
Query Parameters:
address(required): Wallet address (0x format)chain(required): Chain identifier (143for testnet,monadfor mainnet)
Example Request:
GET /api/wallet/balances?address=0x...&chain=143Response:
[
{
"token_address": "0x...",
"name": "Token Name",
"symbol": "SYMBOL",
"logo": "https://...",
"decimals": 18,
"balance": "1000000000000000000",
"usd_price": 1.5,
"usd_value": 1.5
}
]Error Responses:
400: Missing address or chain parameter500: Moralis API error or server error
To add support for other networks, update src/config.ts:
import { yourChain } from "@reown/appkit/networks";
export const wagmiAdapter = new WagmiAdapter({
networks: [yourChain],
transports: {
[yourChain.id]: http(),
},
});Update src/context/NetworkContext.tsx to add new network options:
type Network = "mainnet" | "testnet" | "yournetwork";
const chainId = network === "mainnet"
? "monad"
: network === "testnet"
? "143"
: "your-chain-id";The app uses CSS variables for theming. Edit src/app/globals.css:
:root {
--primary: 222.2 47.4% 11.2%;
--background: 0 0% 100%;
/* ... */
}NFT Display:
// Create src/hooks/useNFTs.ts
export function useNFTs(address?: string) {
// Fetch NFTs from Moralis NFT API
}Transaction History:
// Create src/hooks/useTransactions.ts
export function useTransactions(address?: string) {
// Fetch transactions from Moralis
}Enhanced Search:
// Add ENS name resolution
// Add address book/favorites
// Add search historyThe .w-container class provides consistent width:
.w-container {
max-width: 1280px;
margin: 0 auto;
padding: 0 1rem; /* Responsive */
}1. "MORALIS_API_KEY is not set"
- β
Check
.env.localexists in root directory - β
Verify variable name is exactly
MORALIS_API_KEY - β Restart dev server after adding env vars
2. "NEXT_PUBLIC_PROJECT_ID is not set"
- β
Ensure variable starts with
NEXT_PUBLIC_ - β
Check
.env.localfile - β Restart dev server
3. Wallet Connection Fails
- β Check Reown Project ID is correct
- β Verify wallet extension is installed
- β Check browser console for errors
- β Ensure Monad network is added to wallet
4. No Tokens Displayed
- β Verify wallet has tokens on selected network
- β Check network toggle (testnet vs mainnet)
- β Check browser console for API errors
- β Verify Moralis API key has proper permissions
5. "chain must be a valid enum value"
- β
Ensure using correct chain IDs:
- Testnet:
143 - Mainnet:
monad
- Testnet:
- β Check NetworkContext is providing correct chainId
6. Images Not Loading
- β
Check
next.config.tshas image domains configured - β Some tokens may not have logos (fallback displays first letter)
- β Check browser console for CORS errors
Enable React Query devtools for debugging:
// Add to src/context/index.tsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
// Add inside ContextProvider
<ReactQueryDevtools initialIsOpen={false} />- Push to GitHub
git add .
git commit -m "Initial commit"
git push origin main-
Import to Vercel
- Go to Vercel
- Click "New Project"
- Import your repository
-
Add Environment Variables
MORALIS_API_KEY: Your Moralis API keyNEXT_PUBLIC_PROJECT_ID: Your Reown Project ID
-
Deploy
- Click "Deploy"
- Wait for build to complete
Netlify:
# Build command
npm run build
# Publish directory
.nextRailway/Render:
- Set build command:
npm run build - Set start command:
npm start - Add environment variables in dashboard
Required for Production:
MORALIS_API_KEY: Server-side API keyNEXT_PUBLIC_PROJECT_ID: Client-side project ID
Security Notes:
- β
Never commit
.env.localto git - β Use platform-specific env var management
- β
MORALIS_API_KEYshould never be exposed client-side - β
NEXT_PUBLIC_*variables are exposed to browser
- Moralis API Docs - Complete API reference
- Reown AppKit Docs - Wallet connection guide
- Monad Docs - Monad blockchain documentation
- Next.js Docs - Next.js framework guide
- Wagmi Docs - Ethereum React Hooks
- React Query Docs - Data fetching library
The portfolio viewer includes a powerful search feature that allows you to view any address's portfolio:
-
Without Wallet Connection:
- Enter any Monad address in the search bar
- Click "Search" or press Enter
- View the portfolio for that address
-
With Wallet Connected:
- Default view shows your connected wallet
- Search for other addresses to view their portfolios
- Click "View my wallet instead" to return to your wallet
- Validates Ethereum address format (0x followed by 40 hexadecimal characters)
- Shows error message for invalid addresses
- Only searches when address format is correct
// Search bar automatically validates addresses
// Valid: 0xdAF0182De86F904918Db8d07c7340A1EfcDF8244
// Invalid: 0x123 (too short)
// Invalid: dAF0182De86F904918Db8d07c7340A1EfcDF8244 (missing 0x)- API Key Security: Always keep
MORALIS_API_KEYserver-side only - Error Handling: Always handle API errors gracefully
- Loading States: Show loading indicators for better UX
- Caching: Leverage React Query caching to reduce API calls
- Type Safety: Use TypeScript types for all data structures
- Responsive Design: Test on multiple screen sizes
- Performance: Use Next.js Image component for token logos
- Address Validation: Always validate addresses before making API calls
This template is provided as-is for educational and development purposes.
Contributions are welcome! Please feel free to submit a Pull Request.
- Rate Limits: Moralis free tier has rate limits - implement caching
- API Costs: Monitor your Moralis API usage
- Network Support: Verify Monad network support in Moralis
- Token Logos: Some tokens may not have logos available
- Price Data: Prices may have slight delays (30s cache)
Built with β€οΈ for the Monad ecosystem
Happy Building! π