A production-ready, real-time trading dashboard frontend built with Next.js 14+, TypeScript, Tailwind CSS, and WebSocket integration. This frontend connects to a Node.js/Express backend with PostgreSQL and Redis for a complete stock exchange simulator.
- Next.js 14+ (App Router) - React framework
- TypeScript - Type-safe development
- Tailwind CSS v4 - Styling
- WebSocket API - Real-time updates
- React Context - State management
- Fetch API - REST integration
app/
context/
AuthContext.tsx # Authentication context & useAuth hook
dashboard/
page.tsx # Main dashboard page
login/
page.tsx # Login page
register/
page.tsx # Registration page
layout.tsx # Root layout with AuthProvider
page.tsx # Landing page (redirect)
components/
DashboardHeader.tsx # Header with user info & logout
OrderForm.tsx # Place buy/sell orders
OrderBook.tsx # Real-time order book with bids/asks
TradeTicker.tsx # Real-time trade stream (last 20)
Portfolio.tsx # Cash balance & positions (auto-refresh)
OpenOrders.tsx # Active orders with cancel button
hooks/
useWebSocket.ts # Custom WebSocket hook
lib/
api.ts # REST API helpers with auth
websocket.ts # Production WebSocket client
types.ts # TypeScript interfaces
auth.ts # Auth utilities
proxy.js # Route protection (Next.js 16 proxy)
env.example # Environment variables template
- Registration & Login - Create accounts or sign in with email/password
- JWT Token Management - Secure token storage in localStorage
- Route Protection - Dashboard requires authentication
- Auto-Redirect - Logged-in users bypass login page
- Session Persistence - User stays logged in across page refreshes
- Auto-Reconnect - Exponential backoff with max 5 attempts
- Message Queuing - Queues messages if disconnected
- Event Subscriptions - Clean subscribe/unsubscribe API
- Multiple Event Types:
orderbook:update- Live bid/ask updatestrade:executed- Completed tradesorder:update- Your order status changes
- Place Orders - Buy/Sell with price & quantity
- Order Validation - Client-side validation before submission
- Success/Error Messages - User feedback on actions
- Cancel Orders - One-click cancel on open orders
- Auto-Refresh - Open orders update every 2 seconds
- Top 10 Levels Each Side - Best bids (green) & asks (red)
- Live Updates - Updates on
orderbook:updateevents - Visual Distinction - Color-coded buy/sell sides
- Hover Effects - Interactive row highlighting
- Last 20 Trades - Rolling list of recent executions
- Live Animation - New trades highlight for 1 second
- Buy/Sell Colors - Green for buys, red for sells
- Timestamp Display - Precise execution times
- Cash Balance - Available funds display
- Positions - Holdings by symbol
- Total Value - Portfolio worth calculation
- Auto-Refresh - Updates every 5 seconds
- Position Details - Quantity & average price per position
- Responsive Grid - Adapts from mobile (1 col) → tablet (2 col) → desktop (4 col)
- Fintech Styling - Dark mode with blue/green/red accents
- Connection Status - Real-time WebSocket connection indicator
- Professional UI - Clean, production-ready design
git clone <repo-url>
cd trading-dashboard
npm installCopy the example and set your backend URLs:
cp env.example .env.localEdit .env.local:
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_WS_URL=ws://localhost:3001For production:
NEXT_PUBLIC_API_URL=https://api.exchange.com
NEXT_PUBLIC_WS_URL=wss://api.exchange.comnpm run devVisit http://localhost:3000 and sign up or login.
The frontend expects these endpoints on your backend:
-
POST /auth/register- Create account- Body:
{ email, password } - Response:
{ token, user: { id, email } }
- Body:
-
POST /auth/login- Sign in- Body:
{ email, password } - Response:
{ token, user: { id, email } }
- Body:
GET /portfolio- Get user portfolio- Auth: JWT header
- Response:
{ cash, positions: [{ symbol, quantity, avgPrice }], totalValue }
-
POST /orders- Place order- Auth: JWT header
- Body:
{ symbol, side: 'BUY'|'SELL', price, quantity } - Response:
{ id, symbol, side, price, quantity, remainingQuantity, status, createdAt }
-
GET /orders/open- Get open orders- Auth: JWT header
- Response:
[{ id, symbol, side, price, quantity, remainingQuantity, status, ... }]
-
DELETE /orders/:id- Cancel order- Auth: JWT header
- Response:
{}
Subscribe to events via the useWebSocket hook:
const { isConnected, subscribe, send } = useWebSocket(wsUrl);
// Listen for order book updates
const unsub = subscribe('orderbook:update', (data) => {
console.log('Order book:', data); // { symbol, bids: [...], asks: [...], timestamp }
});
// Listen for new trades
subscribe('trade:executed', (trade) => {
console.log('Trade:', trade); // { symbol, price, quantity, timestamp, side }
});
// Listen for order updates
subscribe('order:update', (order) => {
console.log('Order:', order); // { id, symbol, status, remainingQuantity, ... }
});- Registration: User fills email/password → calls
POST /auth/register - JWT Storage: Token saved to localStorage + API Authorization header
- User State: AuthContext stores user info in localStorage
- Protected Routes: Proxy redirects unauthenticated users to
/login - Logout: Clears localStorage & redirects to
/login
- Connection:
useWebSockethook connects on mount - Auto-Reconnect: Exponential backoff (3s → 6s → 12s → 24s → 48s)
- Event Subscriptions: Components subscribe to events
- Message Handling: Events trigger callbacks → state updates
- Disconnect: Cleanup unsubscribe on unmount
All types are strictly defined in /lib/types.ts:
interface Order {
id: string;
symbol: string;
side: 'BUY' | 'SELL';
price: number;
quantity: number;
remainingQuantity: number;
status: string;
}
interface Trade {
symbol: string;
price: number;
quantity: number;
timestamp: number;
side?: 'BUY' | 'SELL';
}
interface Portfolio {
cash: number;
positions: Position[];
totalValue?: number;
}
interface OrderBook {
symbol: string;
bids: OrderBookLevel[];
asks: OrderBookLevel[];
timestamp: number;
}npm run build
vercel deploySet environment variables in Vercel project settings:
NEXT_PUBLIC_API_URLNEXT_PUBLIC_WS_URL
- Create component in
/components - Use
useWebSocket()for real-time data - Use
api.*functions for REST calls - Export from dashboard page
- Update endpoint in
/lib/api.ts - Update types in
/lib/types.ts - Update components using that endpoint
Use the useAuth() hook anywhere:
import { useAuth } from '@/app/context/AuthContext';
export function MyComponent() {
const { user, isAuthenticated, logout } = useAuth();
// ...
}- API Errors: Caught and displayed in component error states
- WebSocket Errors: Connection status shown in dashboard header
- Auth Errors: Redirect to login on 401 response
- Network Errors: Graceful fallbacks with "Loading..." states
- Lazy Loading: Components mount on dashboard page
- Auto-Refresh: Portfolio (5s), Orders (2s) - configurable intervals
- Event Debouncing: WebSocket updates trigger state changes
- CSS Classes: Tailwind for minimal bundle size
- Production Build: Next.js static optimization
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Mobile browsers with WebSocket support
- Strict TypeScript - No
anytypes - Clean Components - Separation of concerns
- Comments - WebSocket flow, auth handling explained
- Reusable Hooks -
useWebSocket,useAuth - Error Boundaries - Try-catch in async operations
- Check
NEXT_PUBLIC_WS_URLis correct (ws:// for dev, wss:// for prod) - Verify backend WebSocket server is running
- Check browser console for connection logs
- Token expired → user needs to re-login
- Check localStorage has valid token
- Verify backend auth endpoint returns correct JWT
- Wait 5-10 seconds for initial data loads
- Check Network tab in DevTools for API/WebSocket errors
- Verify backend endpoints are implemented
See ../DEVELOPMENT.md for:
- Development setup instructions
- Code style guidelines
- Testing procedures
- Deployment guide
MIT