Skip to main content

React Patterns

Component Patterns​

Functional Components with Hooks​

All components use functional components with hooks. No class components.

interface SecurityTestCardProps {
test: SecurityTest;
onFavorite: (uuid: string) => void;
}

export function SecurityTestCard({ test, onFavorite }: SecurityTestCardProps) {
// Component implementation
}

Path Alias​

Use @/ for imports within frontend/src/:

import { Button } from '@/components/shared/ui/Button';
import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';

Import Ordering​

Group imports: external libraries first, then internal modules:

// External
import { useState, useEffect } from 'react';
import { useDispatch } from 'react-redux';

// Internal
import { Button } from '@/components/shared/ui/Button';
import { fetchTests } from '@/services/api/browser';

Authentication Wrapping​

All authenticated routes use the <RequireAuth> wrapper component, which redirects to Clerk sign-in if the user is not authenticated.


Application Architecture​

The frontend follows a layered provider pattern. Each provider adds a capability (auth, state, theming, analytics) and the tree converges into a single AppRouter that renders page components inside a persistent layout shell.

Provider Hierarchy​

LayerProviderResponsibility
1ClerkProviderAuthentication state, JWT tokens
2Redux ProviderGlobal state management
3AppContentSets up API interceptors via useAuthenticatedApi
4ThemeProviderVisual theme (light/dark, style variants)
5AnalyticsAuthProviderElasticsearch configuration status
6ErrorBoundaryGlobal React error catching
7BrowserRouter / AppRouterURL routing and route guards
info

The two-tier structure (App handles auth providers, AppContent handles theme/analytics/routing) exists because useAuthenticatedApi must run inside ClerkProvider to obtain JWT tokens.

  1. Entry -- main.tsx renders the app in React StrictMode
  2. Authentication -- ClerkProvider handles auth state
  3. State -- Redux Provider makes the store available
  4. Setup -- useAuthenticatedApi configures API interceptors with JWT
  5. Routing -- AppRouter determines which component to render based on URL and permissions
  6. Layout -- Authenticated routes render within the persistent AppLayout (sidebar + top bar)
  7. Guards -- Route guards enforce access control before rendering pages

Routing Structure​

Route Categories​

Public Routes (no authentication required):

  • / -- Hero landing page
  • /sign-in/*, /sign-up/* -- Authentication flows
  • /user-profile -- User profile management

Protected Routes (all share persistent AppLayout):

DestinationRoutesAccess Control
Dashboard/dashboardRequireAuth (per-card degradation, never redirects)
Tests/tests, /tests/:uuidRequireAuth
Analytics/analytics (?tab=, ?expanded=)RequireAuth + AnalyticsProtectedRoute
Agents/agents, /agents/:agentIdRequireAuth + RequireModule module="endpoints"
Tasks/tasksRequireAuth + RequireModule module="endpoints"
Settings/settingsRequireAuth + RequireModule

Pre-2.1 paths still resolve — AppRouter.tsx keeps <Navigate replace> entries for /browser, /browser/test/:uuid, /endpoints/dashboard, /endpoints/agents(/:id), /endpoints/tasks, /favorites, /recent, and /analytics/setup, so bookmarks and older links keep working.

Route Guards​

RequireAuth -- Wraps all authenticated routes; redirects to sign-in if not logged in.

RequireModule -- Enforces role-based access to specific modules. Unauthorized users are redirected to /dashboard.

<RequireAuth>
<RequireModule module="endpoints">
<EndpointsPage />
</RequireModule>
</RequireAuth>

AnalyticsProtectedRoute -- Checks analytics configuration status:

  • Shows loading state during the configuration check
  • Redirects to settings if unconfigured (for users with settings access)
  • Shows an error message for users without settings access

Persistent Layout​

All authenticated routes share a single AppLayout component rendered via React Router's <Outlet> pattern. This keeps the sidebar and top bar mounted across page navigations, improving performance and preserving sidebar collapse state.

Environment Configuration​

The app supports two environment variable strategies for flexible deployment:

StrategyMechanismUse Case
Build-timeimport.meta.env.VITE_*Standard Vite builds
Runtimewindow.__env__ injected by serverDocker / PaaS deploys where env vars change without rebuilding

Runtime variables take precedence when both are present.

Theme System​

One registered theme — the f0 dark palette. <html> carries class="dark" permanently and there is no theme selector.

The useTheme provider survives as a compatibility shim so existing consumers keep compiling: theme is always 'dark', and the style/phosphor state and its localStorage writes are gone. New code should read design tokens directly rather than branching on theme.

See UI Components → Theming for the token layers and the chart-colour guard tests.

Theme CSS variables are defined in Tailwind CSS v4 @theme blocks and drive all component styling. The useTheme hook manages the <html> element classes:

const { theme, themeStyle, setTheme, setThemeStyle, toggleThemeStyle } = useTheme();

Build Configuration​

ToolVersionNotes
Vite7Dev server on port 5173; proxies /api to backend
TypeScriptStrict modetsc -b for type checking
Tailwind CSSv4@theme blocks for CSS variables
React19StrictMode enabled in main.tsx
# Type-check + production build
cd frontend && npm run build # tsc -b && vite build

# Dev server with hot reload
cd frontend && npm run dev # Vite dev server on :5173