Frontend Dashboard
Overview
The frontend is a sophisticated Next.js 14 application designed to provide a fluid, responsive, and data-rich interface to the trading platform. It is engineered to handle high-frequency, real-time data streams without compromising user experience.
Architecture Overview
The frontend uses a dual-state architecture that explicitly separates two types of state:
- Live/Client State (Zustand): Data that changes frequently and is pushed from the server via WebSockets
- Server/Cache State (TanStack Query): Data that is fetched via request-response (e.g., historical data)
Core Components
Real-Time Data Layer (modules/core/)
-
WebSocket Client (
services/ws.ts): A singleton, SSR-safe client that establishes and maintains a persistent connection to the backend’s WebSocket stream. It includes robust logic for exponential backoff and reconnection attempts. -
Event Router (
services/eventRouter.ts): The central hub for all incoming WebSocket messages. Upon receiving an event, it performs a three-step process:- Validation: Uses type guards to ensure the event payload matches a known
EventEnvelope - Deduplication: Uses an
EventDeduperto discard duplicate events - Staging & Routing: Routes the validated event to the appropriate domain store
- Validation: Uses type guards to ensure the event payload matches a known
-
Frame Clock (
shared/lib/frameClock.ts): A simple, fixed-cadence timer (defaulting to 250ms). TheEventRoutersubscribes to its “tick,” and on each tick, it flushes the staged event buffers to the Zustand stores in a single, batched update. -
useRealtimeHook (hooks/useRealtime.ts): A React hook that initializes theEventRouterwhen a component mounts and cleans it up when it unmounts.
State Management
Zustand for Live/Volatile State
Data that changes frequently and is pushed from the server via WebSockets is managed in a set of modular Zustand stores:
- Market Store (
modules/trading/stores/marketStore.ts): Maintains a ring buffer of recent price ticks for each asset, as well as a list of recent trades - Portfolio Store (
modules/trading/stores/portfolioStore.ts): Holds a snapshot of the user’s current portfolio, including total value and positions - Intents & Strategies Stores (
modules/strategy/stores/): Track the state of user-submitted intents and the status of their trading strategies
TanStack Query for Server/Cached State
Data that is fetched via request-response is managed by TanStack Query:
- Query Provider (
shared/query/provider.tsx): The root provider that sets up the query client - Query Keys (
shared/query/keys.ts): A centralized definition of query keys for type safety and consistency
The Synergy: The two libraries work in perfect harmony. Real-time WebSocket events can be used to invalidate TanStack Query’s cache, ensuring the UI always displays fresh data.
UI and Component Architecture
-
Dashboard Composition (
components/trading/trading-dashboard.tsx): The main dashboard is a composite view built from smaller, independent modules. It uses a responsiveDashboardGridto arrange these modules. -
Modular Panels (
modules/dashboard/components/*-module.tsx): Each distinct piece of functionality is encapsulated in its own module component. These components are responsible for fetching their own data and rendering the appropriate UI. -
Theming (
shared/styles/theme.css.ts): The application uses Vanilla Extract for a zero-runtime CSS-in-TS styling solution. It defines avarscontract and implements multiple themes.
Real-Time Data Pipeline
The journey of a real-time update is a carefully orchestrated pipeline designed for resilience and performance.
How It Works
-
WsClient: This singleton class manages the WebSocket connection. It’s SSR-safe and includes robust exponential backoff logic. -
EventRouter: This is the brain of the real-time layer. It subscribes to all messages from theWsClientand processes them through validation, deduplication, and staging. -
FrameClock: This is a simple but critical utility. It’s a global timer that “ticks” every 250 milliseconds. -
Flush to Store: On each tick of the
FrameClock, theEventRoutertakes all the events that have accumulated in its staging buffer and commits them to the appropriate Zustand stores in a single, batched action.
This batching mechanism is the key to a smooth UI. Instead of triggering 100 separate React re-renders for 100 price ticks, it triggers just one render every 250ms with the latest state.
Server-Side Rendering (SSR) and Client Hydration
To avoid showing the user an empty page with loading spinners, we use Next.js’s Server-Side Rendering capabilities.
Code Reference: app/dashboard/page.tsx and modules/dashboard/components/DashboardBootstrap.tsx
The Flow:
- When the user navigates to
/dashboard, the request hits the Next.js server first - The server-side component executes and makes a fast, server-to-server call to fetch an initial snapshot of data
- This initial data is passed as props to the
DashboardBootstrapcomponent - The server renders the complete HTML for the dashboard with this initial data already populated
- On the client, the JavaScript loads and the
DashboardBootstrapcomponent runs itsuseEffecthook, which:- “Hydrates” the client-side Zustand stores with the initial data
- Calls the
useRealtime()hook, which kicks off the real-time data pipeline
From this point on, the client has taken over. The initial server-rendered view seamlessly transitions to a live, WebSocket-powered dashboard.
Styling and Theming
We use Vanilla Extract for a type-safe, zero-runtime CSS-in-TS solution.
-
Theme Contract (
shared/styles/theme.css.ts): We define avarscontract that lists all our design tokens (colors, spacing, etc.) -
Theme Provider (
app/providers/theme/VanillaThemeProvider.tsx): A React context provider that allows the user to switch between themes -
Tailwind CSS Bridge (
shared/styles/base.css.ts): To enable the use of a shared component library that relies on Tailwind’s utility classes, we create a global stylesheet that maps our Vanilla Extract theme variables to CSS custom properties
Component Examples
Dashboard Module
// modules/dashboard/components/portfolio-module.tsx
import { usePortfolioStore } from '@/modules/trading/stores/portfolioStore'
export function PortfolioModule() {
const { portfolio, isLoading } = usePortfolioStore()
if (isLoading) return <div>Loading portfolio...</div>
return (
<div className="portfolio-module">
<h3>Portfolio</h3>
<div className="total-value">
${portfolio.totalValue.toLocaleString()}
</div>
{/* Portfolio details */}
</div>
)
}Real-Time Hook Usage
// components/trading/trading-dashboard.tsx
import { useRealtime } from '@/modules/core/hooks/useRealtime'
export function TradingDashboard() {
// Initialize real-time data flow
useRealtime()
return (
<div className="trading-dashboard">
<PortfolioModule />
<MarketModule />
<IntentsModule />
</div>
)
}Performance Optimizations
Event Batching
The frame clock batching system ensures that high-frequency updates (like market ticks) don’t overwhelm the UI. Multiple updates are coalesced into single React render cycles.
Selective Re-rendering
Zustand’s selector-based subscription model ensures components only re-render when the specific slice of state they care about actually changes.
Request Deduplication
TanStack Query automatically deduplicates identical requests, preventing unnecessary API calls when multiple components need the same data.
Error Handling and Resilience
WebSocket Reconnection
The WebSocket client includes exponential backoff logic for automatic reconnection attempts. If the connection drops, it will attempt to reconnect with increasing delays.
Event Validation
All incoming WebSocket events are validated against TypeScript types before processing. Malformed events are silently dropped to prevent crashes.
Graceful Degradation
If real-time data is unavailable, the UI gracefully falls back to showing cached or static data while indicating the connection status.
Development Tools
React DevTools
Use React DevTools to inspect component hierarchies and state changes in real-time.
Zustand DevTools
Zustand includes built-in DevTools for inspecting store state and actions during development.
Network Tab
Monitor WebSocket connections and real-time event flow in the browser’s Network tab.
Testing
Component Testing
# Run component tests
npm run test:components
# Run with coverage
npm run test:components:coverageIntegration Testing
# Run integration tests
npm run test:integrationE2E Testing
# Run end-to-end tests
npm run test:e2eNext Steps
- Backend Engineers: Return to Backend Engine to understand how the real-time events are generated
- UI/UX Developers: Explore the component library and styling system
- Full-Stack Developers: Continue to Development Guide to learn how to build complete features
- DevOps Engineers: Check Integration & Operations for deployment and monitoring information