Welcome to Section 11! This section focuses on creating modern, responsive web applications for blockchain systems. You'll learn how to build beautiful, functional web interfaces that make blockchain technology accessible to users through intuitive design and real-time data display.
- Modern web development with React and Go
- Responsive design principles and implementation
- Real-time blockchain data display and updates
- User authentication and session management
- WebSocket integration for live updates
- Progressive Web App (PWA) features
This section teaches you how to transform your blockchain backend into a user-friendly web application. You'll build a complete web interface that allows users to interact with the blockchain, view transactions, manage wallets, and monitor network status in real-time.
- Component-based architecture for reusable UI elements
- State management with hooks and context
- Virtual DOM for efficient rendering
- JSX for declarative UI development
- Tailwind CSS for utility-first styling
- Responsive design principles
- Modern CSS features (Grid, Flexbox, Custom Properties)
- Dark/Light theme support
- WebSocket connections for live updates
- Server-Sent Events (SSE) for one-way communication
- RESTful API integration
- GraphQL for efficient data fetching
- Gorilla Mux for routing
- CORS configuration for cross-origin requests
- JWT authentication for secure sessions
- WebSocket support for real-time communication
src/
├── components/
│ ├── Layout/
│ │ ├── Header.jsx
│ │ ├── Sidebar.jsx
│ │ └── Footer.jsx
│ ├── Blockchain/
│ │ ├── BlockExplorer.jsx
│ │ ├── TransactionList.jsx
│ │ └── WalletManager.jsx
│ ├── Dashboard/
│ │ ├── NetworkStatus.jsx
│ │ ├── Statistics.jsx
│ │ └── Charts.jsx
│ └── Common/
│ ├── Button.jsx
│ ├── Modal.jsx
│ └── Loading.jsx
├── pages/
│ ├── Home.jsx
│ ├── Blocks.jsx
│ ├── Transactions.jsx
│ ├── Wallets.jsx
│ └── Network.jsx
├── hooks/
│ ├── useBlockchain.js
│ ├── useWebSocket.js
│ └── useAuth.js
├── services/
│ ├── api.js
│ ├── websocket.js
│ └── auth.js
└── utils/
├── constants.js
├── helpers.js
└── validators.js
// Context for global state
const BlockchainContext = createContext();
// Custom hooks for state management
const useBlockchain = () => {
const [blocks, setBlocks] = useState([]);
const [transactions, setTransactions] = useState([]);
const [wallets, setWallets] = useState([]);
const [networkStatus, setNetworkStatus] = useState({});
return {
blocks,
transactions,
wallets,
networkStatus,
setBlocks,
setTransactions,
setWallets,
setNetworkStatus
};
};import React, { useState, useEffect } from 'react';
import { useBlockchain } from '../hooks/useBlockchain';
const BlockExplorer = () => {
const { blocks, setBlocks } = useBlockchain();
const [loading, setLoading] = useState(true);
const [selectedBlock, setSelectedBlock] = useState(null);
useEffect(() => {
fetchBlocks();
}, []);
const fetchBlocks = async () => {
try {
const response = await fetch('/api/v1/blocks');
const data = await response.json();
setBlocks(data);
} catch (error) {
console.error('Error fetching blocks:', error);
} finally {
setLoading(false);
}
};
return (
<div className="block-explorer">
<h2 className="text-2xl font-bold mb-4">Block Explorer</h2>
{loading ? (
<div className="loading-spinner">Loading blocks...</div>
) : (
<div className="blocks-grid">
{blocks.map((block) => (
<BlockCard
key={block.hash}
block={block}
onClick={() => setSelectedBlock(block)}
/>
))}
</div>
)}
{selectedBlock && (
<BlockModal
block={selectedBlock}
onClose={() => setSelectedBlock(null)}
/>
)}
</div>
);
};
const BlockCard = ({ block, onClick }) => (
<div
className="block-card cursor-pointer hover:shadow-lg transition-shadow"
onClick={onClick}
>
<div className="block-header">
<span className="block-number">#{block.index}</span>
<span className="block-hash">{block.hash.substring(0, 16)}...</span>
</div>
<div className="block-details">
<p>Transactions: {block.transactions.length}</p>
<p>Timestamp: {new Date(block.timestamp).toLocaleString()}</p>
</div>
</div>
);const TransactionList = () => {
const { transactions, setTransactions } = useBlockchain();
const [filter, setFilter] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
const filteredTransactions = transactions.filter(tx => {
const matchesFilter = filter === 'all' || tx.type === filter;
const matchesSearch = tx.id.includes(searchTerm) ||
tx.sender.includes(searchTerm) ||
tx.recipient.includes(searchTerm);
return matchesFilter && matchesSearch;
});
return (
<div className="transaction-list">
<div className="filters">
<input
type="text"
placeholder="Search transactions..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="search-input"
/>
<select
value={filter}
onChange={(e) => setFilter(e.target.value)}
className="filter-select"
>
<option value="all">All Transactions</option>
<option value="BANK">Bank Transactions</option>
<option value="MESSAGE">Message Transactions</option>
<option value="COINBASE">Coinbase Transactions</option>
</select>
</div>
<div className="transactions-table">
<table>
<thead>
<tr>
<th>ID</th>
<th>Type</th>
<th>Sender</th>
<th>Recipient</th>
<th>Amount</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{filteredTransactions.map((tx) => (
<TransactionRow key={tx.id} transaction={tx} />
))}
</tbody>
</table>
</div>
</div>
);
};const WalletManager = () => {
const { wallets, setWallets } = useBlockchain();
const [showCreateWallet, setShowCreateWallet] = useState(false);
const [newWalletName, setNewWalletName] = useState('');
const createWallet = async () => {
try {
const response = await fetch('/api/v1/wallets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: newWalletName }),
});
const wallet = await response.json();
setWallets([...wallets, wallet]);
setShowCreateWallet(false);
setNewWalletName('');
} catch (error) {
console.error('Error creating wallet:', error);
}
};
return (
<div className="wallet-manager">
<div className="wallet-header">
<h2 className="text-2xl font-bold">Wallet Manager</h2>
<button
onClick={() => setShowCreateWallet(true)}
className="create-wallet-btn"
>
Create New Wallet
</button>
</div>
<div className="wallets-grid">
{wallets.map((wallet) => (
<WalletCard key={wallet.address} wallet={wallet} />
))}
</div>
{showCreateWallet && (
<CreateWalletModal
walletName={newWalletName}
setWalletName={setNewWalletName}
onCreate={createWallet}
onClose={() => setShowCreateWallet(false)}
/>
)}
</div>
);
};// WebSocket service
class WebSocketService {
constructor() {
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
connect() {
this.ws = new WebSocket('ws://localhost:8080/ws');
this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
this.ws.onclose = () => {
console.log('WebSocket disconnected');
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
handleMessage(data) {
switch (data.type) {
case 'new_block':
this.updateBlocks(data.block);
break;
case 'new_transaction':
this.updateTransactions(data.transaction);
break;
case 'network_status':
this.updateNetworkStatus(data.status);
break;
default:
console.log('Unknown message type:', data.type);
}
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(`Reconnecting... Attempt ${this.reconnectAttempts}`);
this.connect();
}, 1000 * this.reconnectAttempts);
}
}
send(message) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message));
}
}
}// Custom hook for real-time updates
const useWebSocket = () => {
const { setBlocks, setTransactions, setNetworkStatus } = useBlockchain();
const [wsService] = useState(() => new WebSocketService());
useEffect(() => {
wsService.connect();
// Override message handlers
wsService.updateBlocks = (block) => {
setBlocks(prev => [block, ...prev]);
};
wsService.updateTransactions = (transaction) => {
setTransactions(prev => [transaction, ...prev]);
};
wsService.updateNetworkStatus = (status) => {
setNetworkStatus(status);
};
return () => {
if (wsService.ws) {
wsService.ws.close();
}
};
}, []);
return wsService;
};const AuthContext = createContext();
const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
checkAuthStatus();
}, []);
const checkAuthStatus = async () => {
const token = localStorage.getItem('authToken');
if (token) {
try {
const response = await fetch('/api/v1/auth/verify', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.ok) {
const userData = await response.json();
setUser(userData);
} else {
localStorage.removeItem('authToken');
}
} catch (error) {
console.error('Auth check failed:', error);
localStorage.removeItem('authToken');
}
}
setLoading(false);
};
const login = async (credentials) => {
try {
const response = await fetch('/api/v1/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials),
});
if (response.ok) {
const { token, user } = await response.json();
localStorage.setItem('authToken', token);
setUser(user);
return { success: true };
} else {
return { success: false, error: 'Invalid credentials' };
}
} catch (error) {
return { success: false, error: 'Login failed' };
}
};
const logout = () => {
localStorage.removeItem('authToken');
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout, loading }}>
{children}
</AuthContext.Provider>
);
};const ProtectedRoute = ({ children }) => {
const { user, loading } = useContext(AuthContext);
if (loading) {
return <div className="loading">Loading...</div>;
}
if (!user) {
return <Navigate to="/login" replace />;
}
return children;
};
// Route configuration
const App = () => {
return (
<AuthProvider>
<Router>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
<Route path="/blocks" element={
<ProtectedRoute>
<BlockExplorer />
</ProtectedRoute>
} />
<Route path="/transactions" element={
<ProtectedRoute>
<TransactionList />
</ProtectedRoute>
} />
<Route path="/wallets" element={
<ProtectedRoute>
<WalletManager />
</ProtectedRoute>
} />
</Routes>
</Router>
</AuthProvider>
);
};/* Base styles for mobile */
.block-card {
padding: 1rem;
margin-bottom: 1rem;
border-radius: 0.5rem;
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.blocks-grid {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
}
/* Tablet styles */
@media (min-width: 768px) {
.blocks-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop styles */
@media (min-width: 1024px) {
.blocks-grid {
grid-template-columns: repeat(3, 1fr);
}
.block-card {
padding: 1.5rem;
}
}
/* Large desktop styles */
@media (min-width: 1280px) {
.blocks-grid {
grid-template-columns: repeat(4, 1fr);
}
}const Layout = ({ children }) => {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div className="layout">
<Header onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
<div className="layout-content">
<Sidebar isOpen={sidebarOpen} onClose={() => setSidebarOpen(false)} />
<main className="main-content">
{children}
</main>
</div>
</div>
);
};
const Header = ({ onMenuClick }) => (
<header className="header">
<button
className="menu-button md:hidden"
onClick={onMenuClick}
>
<MenuIcon />
</button>
<h1 className="header-title">Blockchain Explorer</h1>
<div className="header-actions">
<NetworkStatus />
<UserMenu />
</div>
</header>
);In this section, you've learned:
✅ Modern Web Development: React.js with Go backend integration ✅ Responsive Design: Mobile-first, responsive layouts ✅ Real-time Updates: WebSocket integration for live data ✅ Authentication: JWT-based user authentication ✅ Component Architecture: Reusable, maintainable components ✅ State Management: Context API and custom hooks
- React Development: Component-based UI development
- Real-time Communication: WebSocket and live updates
- Responsive Design: Mobile-first, adaptive layouts
- Authentication: Secure user sessions and protected routes
- API Integration: RESTful API consumption and error handling
- State Management: Global state and local component state
- Complete the hands-on exercises below
- Take the quiz to test your understanding
- Move on to Section 12: Mobile App Development
Set up a React application with:
- Create React App or Vite setup
- Tailwind CSS configuration
- Basic component structure
- Routing with React Router
Create a block explorer that:
- Displays blocks in a responsive grid
- Shows block details in a modal
- Implements search and filtering
- Handles loading and error states
Implement real-time features:
- WebSocket connection setup
- Live block updates
- Real-time transaction feed
- Network status monitoring
Build authentication:
- Login/logout functionality
- Protected routes
- JWT token management
- User profile management
Create a responsive dashboard:
- Mobile-first design
- Adaptive layouts
- Touch-friendly interactions
- Performance optimization
Ready to test your knowledge? Take the Section 11 Quiz to verify your understanding of web application development.
Excellent work! You've built a responsive web interface for your blockchain. You're ready to create mobile applications in Section 12! 🚀