
Web3 Frontend
- 234 installs
- 1 repo stars
- Updated January 5, 2026
- pluginagentmarketplace/custom-plugin-blockchain
Build Web3 frontends with wallet connect, contract reads and writes, chain switching, and dApp UX patterns for decentralized applications.
About
Web3 frontend skill from the custom-plugin-blockchain package that guides building decentralized app interfaces with wallet integration, smart-contract interactions, chain management, and production-grade dApp UX patterns.
- Wallet connect and signing flows
- Contract read and write patterns
- Multi-chain and network switching
- dApp UI component guidance
- Transaction state and error UX
Web3 Frontend by the numbers
- 234 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #82 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/pluginagentmarketplace/custom-plugin-blockchain --skill web3-frontendAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 234 |
|---|---|
| repo stars | ★ 1 |
| Last updated | January 5, 2026 |
| Repository | pluginagentmarketplace/custom-plugin-blockchain ↗ |
What it does
Build Web3 frontends with wallet connect, contract reads and writes, chain switching, and dApp UX patterns for decentralized applications.
Files
Web3 Frontend Skill
Master Web3 frontend development with wallet integration, modern libraries (viem/wagmi), and production dApp patterns.
Quick Start
# Invoke this skill for Web3 frontend development
Skill("web3-frontend", topic="wallet", framework="react")Topics Covered
1. Wallet Integration
Connect users to Web3:
- RainbowKit: Beautiful wallet modal
- WalletConnect: Mobile support
- Account Abstraction: ERC-4337
- Multi-chain: Network switching
2. Transaction Management
Handle blockchain interactions:
- Write Operations: Contract writes
- State Tracking: Pending, confirmed
- Gas Estimation: Fee display
- Error Handling: User-friendly messages
3. Signing & Auth
Verify user identity:
- EIP-712: Typed data signing
- SIWE: Sign-In with Ethereum
- Permit: Gasless approvals
- Message Signing: Personal sign
4. React Hooks
Modern patterns with wagmi:
- useAccount: Connection state
- useWriteContract: Transactions
- useReadContract: Data fetching
- useWaitForTransactionReceipt: Confirmations
Code Examples
Connect Wallet
'use client';
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { useAccount } from 'wagmi';
export function WalletConnect() {
const { address, isConnected } = useAccount();
return (
<div>
<ConnectButton />
{isConnected && <p>Connected: {address}</p>}
</div>
);
}Write Contract
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
import { parseEther } from 'viem';
export function MintButton() {
const { writeContract, data: hash, isPending } = useWriteContract();
const { isLoading, isSuccess } = useWaitForTransactionReceipt({ hash });
const mint = () => {
writeContract({
address: '0x...',
abi: [...],
functionName: 'mint',
args: [1n],
value: parseEther('0.08'),
});
};
return (
<button onClick={mint} disabled={isPending || isLoading}>
{isPending ? 'Confirm in wallet...' :
isLoading ? 'Minting...' :
isSuccess ? 'Minted!' : 'Mint NFT'}
</button>
);
}Sign Typed Data (EIP-712)
import { useSignTypedData } from 'wagmi';
const DOMAIN = {
name: 'My App',
version: '1',
chainId: 1,
verifyingContract: '0x...',
};
export function useSignOrder() {
const { signTypedDataAsync } = useSignTypedData();
const sign = async (order: Order) => {
return await signTypedDataAsync({
domain: DOMAIN,
types: { Order: [...] },
primaryType: 'Order',
message: order,
});
};
return { sign };
}Error Handling
export function parseError(error: unknown): string {
const msg = error instanceof Error ? error.message : String(error);
if (msg.includes('user rejected')) return 'Transaction cancelled';
if (msg.includes('insufficient funds')) return 'Insufficient balance';
if (msg.includes('execution reverted')) {
const reason = msg.match(/reason="([^"]+)"/)?.[1];
return reason || 'Transaction would fail';
}
return 'Transaction failed';
}Package Setup
npm install wagmi viem @rainbow-me/rainbowkit @tanstack/react-query// providers/Web3.tsx
import { WagmiProvider } from 'wagmi';
import { RainbowKitProvider } from '@rainbow-me/rainbowkit';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { config } from './config';
const queryClient = new QueryClient();
export function Web3Provider({ children }) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider>{children}</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
}Common Patterns
| Pattern | Use Case | Hook |
|---|---|---|
| Connect wallet | User auth | useAccount |
| Read data | Display balances | useReadContract |
| Write tx | Mint, transfer | useWriteContract |
| Wait for tx | Confirm state | useWaitForTransactionReceipt |
| Sign message | Auth, permit | useSignMessage |
Common Pitfalls
| Pitfall | Issue | Solution |
|---|---|---|
| Hydration error | SSR mismatch | Use dynamic with ssr: false |
| BigInt serialization | JSON.stringify | Custom serializer |
| Stale data | Cache issues | Use refetchInterval |
Troubleshooting
"Wallet not connecting"
// Ensure client-side only
import dynamic from 'next/dynamic';
const ConnectButton = dynamic(
() => import('./ConnectButton'),
{ ssr: false }
);"Transaction pending forever"
Check gas settings or speed up:
await wallet.sendTransaction({
...tx,
maxFeePerGas: tx.maxFeePerGas * 120n / 100n,
});Security Checklist
- [ ] Never expose private keys
- [ ] Validate contract addresses
- [ ] Sanitize user inputs
- [ ] Use proper error boundaries
- [ ] Implement CSP headers
Cross-References
- Bonded Agent:
05-web3-frontend - Related Skills:
ethereum-development,solidity-development
Version History
| Version | Date | Changes |
|---|---|---|
| 2.0.0 | 2025-01 | Production-grade with wagmi v2, viem |
| 1.0.0 | 2024-12 | Initial release |
# web3-frontend Configuration
# Category: general
# Generated: 2025-12-30
skill:
name: web3-frontend
version: "1.0.0"
category: general
settings:
# Default settings for web3-frontend
enabled: true
log_level: info
# Category-specific defaults
validation:
strict_mode: false
auto_fix: false
output:
format: markdown
include_examples: true
# Environment-specific overrides
environments:
development:
log_level: debug
validation:
strict_mode: false
production:
log_level: warn
validation:
strict_mode: true
# Integration settings
integrations:
# Enable/disable integrations
git: true
linter: true
formatter: true
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "web3-frontend Configuration Schema",
"type": "object",
"properties": {
"skill": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$"
},
"category": {
"type": "string",
"enum": [
"api",
"testing",
"devops",
"security",
"database",
"frontend",
"algorithms",
"machine-learning",
"cloud",
"containers",
"general"
]
}
},
"required": [
"name",
"version"
]
},
"settings": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"default": true
},
"log_level": {
"type": "string",
"enum": [
"debug",
"info",
"warn",
"error"
]
}
}
}
},
"required": [
"skill"
]
}Web3 Frontend Guide
Overview
This guide provides comprehensive documentation for the web3-frontend skill in the custom-plugin-blockchain plugin.
Category: General
Quick Start
Prerequisites
- Familiarity with general concepts
- Development environment set up
- Plugin installed and configured
Basic Usage
# Invoke the skill
claude "web3-frontend - [your task description]"
# Example
claude "web3-frontend - analyze the current implementation"Core Concepts
Key Principles
1. Consistency - Follow established patterns 2. Clarity - Write readable, maintainable code 3. Quality - Validate before deployment
Best Practices
- Always validate input data
- Handle edge cases explicitly
- Document your decisions
- Write tests for critical paths
Common Tasks
Task 1: Basic Implementation
# Example implementation pattern
def implement_web3_frontend(input_data):
"""
Implement web3-frontend functionality.
Args:
input_data: Input to process
Returns:
Processed result
"""
# Validate input
if not input_data:
raise ValueError("Input required")
# Process
result = process(input_data)
# Return
return resultTask 2: Advanced Usage
For advanced scenarios, consider:
- Configuration customization via
assets/config.yaml - Validation using
scripts/validate.py - Integration with other skills
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Skill not found | Not installed | Run plugin sync |
| Validation fails | Invalid config | Check config.yaml |
| Unexpected output | Missing context | Provide more details |
Related Resources
- SKILL.md - Skill specification
- config.yaml - Configuration options
- validate.py - Validation script
---
Last updated: 2025-12-30
Web3 Frontend Patterns
Design Patterns
Pattern 1: Input Validation
Always validate input before processing:
def validate_input(data):
if data is None:
raise ValueError("Data cannot be None")
if not isinstance(data, dict):
raise TypeError("Data must be a dictionary")
return TruePattern 2: Error Handling
Use consistent error handling:
try:
result = risky_operation()
except SpecificError as e:
logger.error(f"Operation failed: {e}")
handle_error(e)
except Exception as e:
logger.exception("Unexpected error")
raisePattern 3: Configuration Loading
Load and validate configuration:
import yaml
def load_config(config_path):
with open(config_path) as f:
config = yaml.safe_load(f)
validate_config(config)
return configAnti-Patterns to Avoid
❌ Don't: Swallow Exceptions
# BAD
try:
do_something()
except:
pass✅ Do: Handle Explicitly
# GOOD
try:
do_something()
except SpecificError as e:
logger.warning(f"Expected error: {e}")
return default_valueCategory-Specific Patterns: General
Recommended Approach
1. Start with the simplest implementation 2. Add complexity only when needed 3. Test each addition 4. Document decisions
Common Integration Points
- Configuration:
assets/config.yaml - Validation:
scripts/validate.py - Documentation:
references/GUIDE.md
---
Pattern library for web3-frontend skill
#!/usr/bin/env python3
"""
Validation script for web3-frontend skill.
Category: general
"""
import os
import sys
import yaml
import json
from pathlib import Path
def validate_config(config_path: str) -> dict:
"""
Validate skill configuration file.
Args:
config_path: Path to config.yaml
Returns:
dict: Validation result with 'valid' and 'errors' keys
"""
errors = []
if not os.path.exists(config_path):
return {"valid": False, "errors": ["Config file not found"]}
try:
with open(config_path, 'r') as f:
config = yaml.safe_load(f)
except yaml.YAMLError as e:
return {"valid": False, "errors": [f"YAML parse error: {e}"]}
# Validate required fields
if 'skill' not in config:
errors.append("Missing 'skill' section")
else:
if 'name' not in config['skill']:
errors.append("Missing skill.name")
if 'version' not in config['skill']:
errors.append("Missing skill.version")
# Validate settings
if 'settings' in config:
settings = config['settings']
if 'log_level' in settings:
valid_levels = ['debug', 'info', 'warn', 'error']
if settings['log_level'] not in valid_levels:
errors.append(f"Invalid log_level: {settings['log_level']}")
return {
"valid": len(errors) == 0,
"errors": errors,
"config": config if not errors else None
}
def validate_skill_structure(skill_path: str) -> dict:
"""
Validate skill directory structure.
Args:
skill_path: Path to skill directory
Returns:
dict: Structure validation result
"""
required_dirs = ['assets', 'scripts', 'references']
required_files = ['SKILL.md']
errors = []
# Check required files
for file in required_files:
if not os.path.exists(os.path.join(skill_path, file)):
errors.append(f"Missing required file: {file}")
# Check required directories
for dir in required_dirs:
dir_path = os.path.join(skill_path, dir)
if not os.path.isdir(dir_path):
errors.append(f"Missing required directory: {dir}/")
else:
# Check for real content (not just .gitkeep)
files = [f for f in os.listdir(dir_path) if f != '.gitkeep']
if not files:
errors.append(f"Directory {dir}/ has no real content")
return {
"valid": len(errors) == 0,
"errors": errors,
"skill_name": os.path.basename(skill_path)
}
def main():
"""Main validation entry point."""
skill_path = Path(__file__).parent.parent
print(f"Validating web3-frontend skill...")
print(f"Path: {skill_path}")
# Validate structure
structure_result = validate_skill_structure(str(skill_path))
print(f"\nStructure validation: {'PASS' if structure_result['valid'] else 'FAIL'}")
if structure_result['errors']:
for error in structure_result['errors']:
print(f" - {error}")
# Validate config
config_path = skill_path / 'assets' / 'config.yaml'
if config_path.exists():
config_result = validate_config(str(config_path))
print(f"\nConfig validation: {'PASS' if config_result['valid'] else 'FAIL'}")
if config_result['errors']:
for error in config_result['errors']:
print(f" - {error}")
else:
print("\nConfig validation: SKIPPED (no config.yaml)")
# Summary
all_valid = structure_result['valid']
print(f"\n==================================================")
print(f"Overall: {'VALID' if all_valid else 'INVALID'}")
return 0 if all_valid else 1
if __name__ == "__main__":
sys.exit(main())