#
Frontend
The QuickDapp frontend is a modern React application built with cutting-edge technologies for an optimal developer experience and user interface. It provides seamless Web3 integration, real-time updates, and a responsive design system.
#
Technology Stack
- React 19 - Latest React with concurrent features and modern hooks
- Vite - Lightning-fast build tool and development server
- TypeScript - Full type safety across the application
- TailwindCSS - Utility-first CSS framework
- Radix UI - Unstyled, accessible UI components
- RainbowKit - Beautiful wallet connection UI
- Wagmi - React hooks for Ethereum
- Viem - TypeScript Ethereum library
- React Query - Powerful data synchronization
- React Router - Client-side routing
#
Key Features
#
Web3 Integration
Complete Web3 functionality with wallet connections, transaction handling, and blockchain interactions:
import { useAccount, useConnect, useDisconnect } from 'wagmi'
import { ConnectButton } from '@rainbow-me/rainbowkit'
export function WalletConnection() {
return (
<ConnectButton.Custom>
{({ account, chain, openConnectModal, mounted }) => (
<div>
{!account ? (
<button onClick={openConnectModal}>Connect Wallet</button>
) : (
<div>Connected: {account.displayName}</div>
)}
</div>
)}
</ConnectButton.Custom>
)
}
#
Real-time Data
React Query integration for efficient data fetching and caching:
import { useQuery } from '@tanstack/react-query'
import { graphqlClient } from '../lib/graphql'
export function useTokens() {
return useQuery({
queryKey: ['tokens'],
queryFn: async () => {
const data = await graphqlClient.request(`
query GetTokens {
tokens {
id
address
name
symbol
createdAt
}
}
`)
return data.tokens
}
})
}
#
Responsive Design
TailwindCSS with mobile-first design principles:
export function TokenCard({ token }: { token: Token }) {
return (
<div className="bg-white rounded-lg shadow-md p-4 sm:p-6
border border-gray-200 hover:shadow-lg
transition-shadow duration-200">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900">
{token.name}
</h3>
<span className="text-sm text-gray-500 font-mono">
{token.symbol}
</span>
</div>
<p className="text-sm text-gray-600 font-mono break-all">
{token.address}
</p>
</div>
)
}
#
Component System
Reusable components built on Radix UI primitives:
import * as Dialog from '@radix-ui/react-dialog'
import { Button } from './Button'
export function Modal({
isOpen,
onClose,
title,
children
}: ModalProps) {
return (
<Dialog.Root open={isOpen} onOpenChange={onClose}>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/50" />
<Dialog.Content className="fixed top-1/2 left-1/2 transform
-translate-x-1/2 -translate-y-1/2
bg-white rounded-lg p-6 shadow-xl
max-w-md w-full">
<Dialog.Title className="text-lg font-semibold mb-4">
{title}
</Dialog.Title>
{children}
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
)
}
#
Project Structure
src/client/
├── components/ # Reusable UI components
│ ├── ui/ # Base UI components (Button, Input, etc.)
│ ├── forms/ # Form components
│ └── layout/ # Layout components
├── pages/ # Application pages/routes
├── hooks/ # Custom React hooks
│ ├── useAuth.ts # Authentication hook
│ ├── useTokens.ts # Token data hooks
│ └── useWebSocket.ts # WebSocket connection
├── lib/ # Client-side utilities
│ ├── graphql.ts # GraphQL client setup
│ ├── wagmi.ts # Wagmi configuration
│ └── utils.ts # General utilities
├── styles/ # Global styles
└── types/ # TypeScript type definitions
#
Documentation Sections
- Components - UI component library and usage
- Forms - Form handling and validation
- Global - Global state management and context
- GraphQL - GraphQL client integration
- Web3 - Blockchain interactions and wallet integration
#
Quick Examples
#
Custom Hook for Contract Interaction
// src/client/hooks/useTokenContract.ts
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
import { ERC20_ABI } from '../lib/abis'
export function useTokenTransfer(tokenAddress: string) {
const { writeContract, data: hash, isPending } = useWriteContract()
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
hash,
})
const transfer = (to: string, amount: bigint) => {
writeContract({
address: tokenAddress as `0x${string}`,
abi: ERC20_ABI,
functionName: 'transfer',
args: [to, amount],
})
}
return {
transfer,
isPending,
isConfirming,
isSuccess,
hash
}
}
#
Realtime Notifications with WebSockets
GraphQL is used for authentication and notifications querying/mutations. On-chain interactions (including token deployment) are done via viem/wagmi on the client, not via GraphQL mutations.
Use WebSockets to react to new notifications as they arrive:
// src/client/hooks/useNotificationsSocket.ts
import { useEffect } from 'react'
export function useNotificationsSocket(onNotification: (n: any) => void) {
useEffect(() => {
const token = localStorage.getItem('auth-token')
if (!token) return
const proto = location.protocol === 'https:' ? 'wss' : 'ws'
const ws = new WebSocket(`${proto}://${location.host}/ws?token=${encodeURIComponent(token)}`)
ws.onmessage = (evt) => {
try {
const msg = JSON.parse(evt.data)
if (msg?.type === 'NotificationReceived' || msg?.type === 1) {
onNotification(msg.data)
}
} catch {}
}
return () => ws.close()
}, [onNotification])
}
#
Form Component with Validation
// src/client/components/forms/DeployTokenForm.tsx
import { useForm } from 'react-hook-form'
import { Button } from '../ui/Button'
import { Input } from '../ui/Input'
import { useDeployToken } from '../../hooks/useDeployToken'
interface DeployTokenFormData {
name: string
symbol: string
initialSupply: string
}
export function DeployTokenForm({ onSuccess }: { onSuccess?: () => void }) {
const {
register,
handleSubmit,
formState: { errors, isValid },
reset
} = useForm<DeployTokenFormData>()
const deployToken = useDeployToken()
const onSubmit = async (data: DeployTokenFormData) => {
try {
await deployToken.mutateAsync(data)
reset()
onSuccess?.()
} catch (error) {
console.error('Failed to deploy token:', error)
}
}
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<Input
label="Token Name"
{...register('name', { required: 'Token name is required' })}
error={errors.name?.message}
placeholder="My Token"
/>
</div>
<div>
<Input
label="Symbol"
{...register('symbol', {
required: 'Symbol is required',
maxLength: { value: 6, message: 'Symbol must be 6 characters or less' }
})}
error={errors.symbol?.message}
placeholder="MTK"
/>
</div>
<div>
<Input
label="Initial Supply"
type="number"
{...register('initialSupply', {
required: 'Initial supply is required',
min: { value: 1, message: 'Supply must be at least 1' }
})}
error={errors.initialSupply?.message}
placeholder="1000000"
/>
</div>
<Button
type="submit"
disabled={!isValid || deployToken.isPending}
loading={deployToken.isPending}
>
Deploy Token
</Button>
</form>
)
}
#
Layout Component
// src/client/components/layout/AppLayout.tsx
import { ReactNode } from 'react'
import { Header } from './Header'
import { Sidebar } from './Sidebar'
import { NotificationCenter } from '../NotificationCenter'
import { useAuth } from '../../hooks/useAuth'
interface AppLayoutProps {
children: ReactNode
}
export function AppLayout({ children }: AppLayoutProps) {
const { user } = useAuth()
return (
<div className="min-h-screen bg-gray-50">
<Header />
<div className="flex">
{user && <Sidebar />}
<main className="flex-1 p-6">
{children}
</main>
</div>
<NotificationCenter />
</div>
)
}
The frontend architecture emphasizes modern React patterns, excellent type safety, and seamless Web3 integration while maintaining a clean, maintainable codebase.