Frontend Architecture
Component structure, routing, Redux, API layer, and Socket.IO integration.
This document is the entry point for understanding how the frontend is structured. For the backend's view of the same system, see dare-backend/docs/architecture.md.
Component Diagram
flowchart TB
browser["Browser"]
subgraph frontend["DARE Frontend"]
router["React Router v6\nRoutes -> Pages -> Feature Components"]
features["Feature Components\nConversation, WorkflowBuilder, FileManager, Artifacts, Auth, Layout"]
ui["UI Primitives\nRadix, Tailwind, brand tokens"]
redux["Redux Toolkit Store\nDomain slices + async thunks"]
middleware["Socket.IO Middleware\n/chat namespace\n/workflow namespace"]
api["API Layer\nAxios + Zod validation"]
end
backend["DARE Backend"]
browser --> frontend
router --> features
features --> ui
features --> redux
redux --> api
redux --> middleware
api <-->|REST over HTTPS| backend
middleware <-->|Socket.IO over WSS| backendState Management
Redux Toolkit organizes state by domain, with two Socket.IO middleware files for real-time channels:
src/redux/
├── slices/ # State slices by domain
│ ├── userSlice.ts # Auth + preferences
│ ├── conversationSlice.ts # Chat sessions and messages
│ ├── fileSlice.ts # Uploads and processing status
│ ├── workflowSlice.ts # Workflow definitions and runs
│ ├── promptSlice.ts # Prompt templates
│ ├── billingSlice.ts # Token usage and cost
│ ├── tagsSlice.ts # File tags
│ ├── themeSlice.ts # Light/dark mode
│ ├── notificationSlice.ts # Toast notifications
│ └── websocketSlice.ts # Connection state
├── asyncThunks/ # createAsyncThunk wrappers, one file per domain
├── middleware/
│ ├── socketMiddleware.ts # /chat namespace — chat, messages, artifacts, voice
│ └── workflowSocketMiddleware.ts # /workflow namespace — execution, steps, batches
├── types/ # TypeScript interfaces matching API response shapes
└── workflowBuilder/ # Canvas state — nodes, edges, undo/redoPatterns:
- All slices use
createSlicewith reducers. - Async operations use
createAsyncThunk, never inlinedispatch(fn). - Type-safe hooks via
useSelector<RootState>anduseDispatch<AppDispatch>. - Middleware owns Socket.IO lifecycle — connect, reconnect, dispatch on event.
Socket.IO Integration
Two namespaces, two middleware files, one connection lifecycle each.
| Namespace | Middleware | Used by |
|---|---|---|
/chat | socketMiddleware.ts | Streaming completions, artifact emission, message acks |
/workflow | workflowSocketMiddleware.ts | DAG execution, per-step progress, batch run updates |
Validation rule: All incoming /workflow events are validated with Zod schemas in src/schemas/workflowSocket.ts before being dispatched to Redux. This catches backend-side schema drift early and surfaces a typed error instead of a silently broken UI.
For the full event contract — payload shapes, directions, error codes — see the backend's Socket.IO event reference.
API Layer
src/api/ # One file per domain (auth, conversation, files, ...)
src/redux/types/ # TypeScript interfaces matching API response shapes
src/redux/asyncThunks/ # createAsyncThunk wrappers, one file per domainIntegration cycle:
Component → dispatch(thunk) → api/<domain>.ts → axios → Backend →
thunk fulfilled → slice handles state → component re-rendersCentralized concerns:
- Base URL & headers in
src/api/config.ts. - Token refresh via axios response interceptor.
- Error normalization in
src/utils/errorHandler.ts— extracts user-friendly messages from DRF error shapes.
Component Organization
src/components/
├── ui/ # Shadcn/ui primitives (Button, Dialog, Input, ...) — reusable
├── Auth/ # Login, register, password reset
├── Conversation/ # Chat view, message list, composer
├── WorkflowBuilder/ # Canvas, nodes, edges, run controls
├── FileManager/ # Upload, list, tag, folder
├── Artifacts/ # Code blocks, diagrams, document previews
├── Dashboard/ # Analytics overview
└── Layout/ # App shell, sidebar, headerFeature folders follow the same internal layout: index.tsx, sub-components, hooks (useX.ts), and feature-specific types co-located.
Routing
<Route path="/login" element={<LoginScreen />} />
<Route path="/dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />ProtectedRoute checks the auth slice and redirects to /login if no valid token. A route listener (src/routes/RouteListener.tsx) handles cross-cutting concerns like analytics and toast cleanup on navigation.
Styling
- Tailwind as the base utility system.
- Shadcn/ui for accessible component primitives, customized via Tailwind tokens.
- CSS custom properties drive light/dark theming — toggling adds/removes the
darkclass on<html>. - Brand colors and the DARE gradient live in
tailwind.config.ts. - Class merging via the
cn()helper (clsx+tailwind-merge).
Forms
Formik + Yup for non-trivial forms. Validation schemas are defined alongside the form component and shared with the backend's serializer-level validation where useful. Simple inputs use plain controlled state.
TypeScript
- Strict mode on.
- Interfaces preferred over type aliases for object shapes.
- API response types in
src/redux/types/are the source of truth for shape — update them when the backend changes.
Performance
- Route-level code splitting (
React.lazy+Suspense). useMemo/useCallbackfor expensive renders or stable references passed to memoized children.- Virtualization (
react-virtualor similar) for long lists where applicable. - Vite's default asset fingerprinting + immutable cache headers in production (see DEPLOYMENT.md).
Build & Bundle
- Vite for dev server and production builds.
- Path alias
@/→src/. - Client error reporting via Sentry when
VITE_SENTRY_DSNis configured.
Further reading
- docs/configuration.md — Vite environment variables
- CONTRIBUTING.md — coding standards and PR process
- docs/RULES.md — project-specific conventions
- dare-backend/docs/architecture/socketio-events.md — full event contract