Skip to content

Web Application

The Relate Mail web application is a single-page app built with React 19, TypeScript, and Vite 7.3. It provides a full-featured email client in the browser with real-time updates, composing and sending mail, label and filter management, and user preferences.

Screenshot

[Screenshot placeholder: Inbox view]

TODO: Add screenshot of the inbox view showing the email list, search bar, and detail panel

Technology Stack

ConcernLibraryVersion
UI frameworkReact19.2
LanguageTypeScript5.9
Build toolVite7.3
RoutingTanStack Router1.x (file-based)
Server stateTanStack Query5.x
Client stateJotai2.x
CSS frameworkTailwind CSS4.1
Component variantsCVA (class-variance-authority)0.7
UI primitivesRadix UIlatest
IconsLucide React0.563
Real-timeSignalR (@microsoft/signalr)8.x
Authreact-oidc-context + oidc-client-ts3.x
TestingVitest + MSW + Playwrightlatest

Project Structure

web/
  src/
    api/              # API client, TanStack Query hooks, SignalR connection, types
    auth/             # OIDC AuthProvider wrapper
    components/
      mail/           # Email-specific components (list, detail, search, labels, etc.)
      filters/        # Filter builder components
      ui/             # Shadcn/ui components (badge, button, card, dialog, etc.)
    lib/              # Utilities, logger
    routes/           # TanStack Router file-based routes
    test/             # Test setup, mocks, factories
    config.ts         # Runtime configuration loader
    main.tsx          # Application entry point
  e2e/                # Playwright E2E tests
  public/             # Static assets

Development

Start the dev server:

bash
cd web
npm run dev

The Vite dev server runs on port 5492 and proxies all /api requests to the backend at http://localhost:5000 (configurable via VITE_API_PROXY_URL environment variable). This avoids CORS issues during local development.

Path Alias

The @/ path alias maps to src/, so imports look like:

typescript
import { api } from '@/api/client'
import { Button } from '@/components/ui/button'

This is configured in both vite.config.ts (for Vite) and tsconfig.json (for TypeScript).

Build

Create a production build:

bash
cd web
npm run build

This runs TypeScript type checking (tsc -b) followed by Vite's production bundler. Output goes to dist/.

Key Architectural Decisions

File-Based Routing

Routes are defined as files in src/routes/. The TanStack Router plugin for Vite (@tanstack/router-plugin/vite) automatically generates routeTree.gen.ts from these files. Never edit the generated file manually -- it is regenerated on every dev server restart and build.

See Routing for the complete route reference.

Server State vs. Client State

The app separates concerns between two state layers:

  • TanStack Query manages all server-derived state: emails, profile, labels, filters, preferences, SMTP credentials. Data is fetched via hooks, cached, and automatically invalidated when mutations succeed.
  • Jotai manages ephemeral client-only state: UI toggles, selected email ID, search input, pagination cursors. Jotai atoms are lightweight and avoid the boilerplate of context providers.

Real-Time Updates via SignalR

The inbox route establishes a SignalR connection to /hubs/email on mount. When the server pushes events (new email, status change, deletion), the SignalR handler invalidates the relevant TanStack Query cache entries, causing the UI to refetch and update seamlessly.

See SignalR Integration for details.

Shared Component Library

UI and mail components are imported from @relate/shared wherever possible. The web app's src/components/ directory contains either re-exports of shared components or web-specific components that are not needed by the desktop or mobile clients.

See Shared Package for the full component and utility reference.

Available Scripts

CommandDescription
npm run devStart Vite dev server on port 5492
npm run buildProduction build (type check + bundle)
npm run lintRun ESLint
npm run testVitest in watch mode
npm run test:runVitest single run
npm run test:coverageVitest with coverage report
npm run test:e2ePlaywright E2E tests
npm run test:e2e:uiPlaywright in headed/UI mode
npm run test:e2e:installInstall Playwright browsers
npm run previewPreview production build locally

Released under the MIT License.