Skip to content

Web Application Overview

The Jubiloop web application is a modern React SPA built with TanStack Router, featuring type-safe routing, authentication, and responsive UI components.

Technology Stack

  • Framework: React 19 with TypeScript
  • Build Tool: Vite
  • Routing: TanStack Router (file-based)
  • State Management: Zustand
  • API Client: TanStack Query
  • UI Components: shadcn/ui with Tailwind CSS
  • Testing: Vitest and React Testing Library

Architecture Overview

Core Concepts

  1. File-Based Routing

    • Routes defined in /src/routes/ directory
    • Automatic code splitting per route
    • Type-safe navigation and parameters
  2. Authentication Flow

    • Session-based auth with HTTP-only cookies
    • Protected route groups: (auth) vs (public)
    • Automatic redirects based on auth state
  3. State Management Strategy

    • Zustand for auth and global app state
    • TanStack Query for server state caching
    • Local React state for component UI
  4. API Integration

    • Type-safe API client
    • Automatic request/response handling
    • Optimistic updates for better UX

Project Structure

src/
├── routes/                 # TanStack Router file-based routes
│   ├── __root.tsx         # Root layout with auth logic
│   ├── index.tsx          # Home page (redirects to dashboard)
│   ├── (public)/          # Unauthenticated route group
│   │   ├── sign-in.tsx    # Login page
│   │   └── sign-up.tsx    # Registration page
│   └── (auth)/            # Authenticated route group
│       └── dashboard/     # Dashboard pages
│           └── route.tsx
├── components/
│   ├── auth/              # Authentication components
│   │   ├── SignInForm.tsx
│   │   └── SignUpForm.tsx
│   └── Header.tsx         # Main navigation
├── hooks/
│   └── auth/              # Authentication hooks
│       └── useSession.ts
├── stores/
│   └── authStore.ts       # Zustand auth store
├── lib/
│   ├── api-client.ts      # API client setup
│   └── tanstack-query.ts  # Query client config
└── main.tsx               # App entry with router setup

TanStack Router Configuration

Router Setup (main.tsx)

typescript
const router = createRouter({
  routeTree,
  context: {
    ...TanstackQuery.getContext(),
  },
  defaultPreload: 'intent',
  scrollRestoration: true,
  defaultStructuralSharing: true,
  defaultPreloadStaleTime: 0,
})

Route Structure

  1. Root Route (__root.tsx)

    • Handles authentication checks
    • Provides layout wrapper
    • Manages redirects
  2. Public Routes ((public)/)

    • Sign in/up pages
    • Accessible without auth
    • Auto-redirect if already authenticated
  3. Protected Routes ((auth)/)

    • Dashboard and app features
    • Requires authentication
    • Auto-redirect to login if not authenticated

Route Generation

Routes are automatically generated by TanStack Router:

  • Source: /src/routes/ directory
  • Output: /src/routeTree.gen.ts (auto-generated)
  • Never edit the generated file directly

Features

Authentication System

  1. Session Management

    • HTTP-only cookies for security
    • Automatic session refresh
    • Remember me functionality
  2. Protected Routes

    • Route-level auth checks
    • Automatic redirects
    • Preserved intended destination
  3. Auth State

    • Global auth store (Zustand)
    • Persisted user data
    • Loading states
typescript
// Declarative navigation
import { Link } from '@tanstack/react-router'
<Link to="/dashboard">Dashboard</Link>

// Programmatic navigation
import { useNavigate } from '@tanstack/react-router'
const navigate = useNavigate()
navigate({ to: '/dashboard' })

UI/UX

  • Responsive design

Performance

  • Automatic code splitting per route
  • Lazy loading with React.lazy
  • Optimistic UI updates
  • Request deduplication

Development Workflow

Available Scripts

bash
npm run dev          # Start dev server (Vite)
npm run build        # Build for production
npm run preview      # Preview production build
npm run test         # Run tests with Vitest
npm run lint         # Run ESLint
npm run lint-fix     # Fix ESLint issues
npm run format       # Format code with Prettier
npm run check        # Run all checks (lint + format)
npm run check-types  # TypeScript type checking

Pre-commit Hooks

The project includes pre-commit hooks that:

  • Enforce Node.js v22+ for development
  • Run lint-staged on staged files
  • Prevent commits if checks fail

Best Practices

  1. Routing

    • Use file-based routing for organization
    • Group related routes with folders
    • Keep route components focused
  2. State Management

    • Use Zustand sparingly for global state
    • Prefer server state with TanStack Query
    • Local state for component-specific UI
  3. Component Design

    • Small, focused components
    • Composition over complex components
    • Proper TypeScript types
  4. Performance

    • Implement route-level code splitting
    • Use React.memo for expensive components
    • Optimize re-renders with proper dependencies

API Integration

Shared API Client Architecture

The webapp uses the @jubiloop/api-client package, a shared HTTP client that provides consistency across all frontend applications while allowing client-specific customizations.

Package Features

The @jubiloop/api-client package (located in /packages/api-client/) provides:

  • Automatic CSRF Protection: Injects CSRF tokens for state-changing requests (POST, PUT, PATCH, DELETE)
  • Session-based Authentication: Supports cookies with withCredentials enabled by default
  • Flexible Configuration: Each app can customize behavior while using the same core
  • Pre-configured Hooks: TanStack Query hooks ready to use
  • TypeScript Support: Full type safety with interfaces for all configurations

Webapp Configuration

Located in src/lib/api/setup.ts:

typescript
import { createApiPackage } from '@jubiloop/api-client'

export const api = createApiPackage({
  baseURL: import.meta.env.VITE_API_URL || 'https://api.jubiloop.localhost',
  onUnauthorized: () => {
    // Webapp-specific: invalidate auth queries on 401
    queryClient.invalidateQueries({ queryKey: queryKeys.auth.all })
  },
})

// Export hooks and client for use in components
export const { useHealthCheck, useDetailedHealth } = api.hooks
export const apiClient = api.client

Key Webapp Customizations:

  • Auth Invalidation: Automatically invalidates auth queries on 401 responses
  • Session Management: Maintains user sessions through cookie-based auth
  • Query Cache Integration: Works seamlessly with TanStack Query's cache

TanStack Query Setup

Located in src/integrations/tanstack-query/:

  • Query client configuration
  • Default stale times
  • Cache management
  • Global error handling

Data Fetching Pattern

typescript
// Using the shared API client
import { apiClient } from '@/lib/api/setup'
import { useQuery } from '@tanstack/react-query'

const { data, isLoading, error } = useQuery({
  queryKey: ['events'],
  queryFn: () => apiClient.get('/events'),
})

// Or using pre-configured hooks from the package
import { useHealthCheck } from '@/lib/api/setup'

const health = useHealthCheck()

Authentication Implementation

Session Hook

Located in src/hooks/auth/useSession.ts:

  • Checks session validity
  • Handles token refresh
  • Provides loading states

Auth Store

Located in src/stores/authStore.ts:

  • User data management
  • Login/logout actions
  • Persistent state

Protected Route Pattern

typescript
// In __root.tsx
beforeLoad: async ({ context, location }) => {
  const session = await checkSession()
  if (!session && isProtectedRoute(location)) {
    throw redirect({ to: '/sign-in' })
  }
}

Testing Strategy

  1. Unit Tests

    • Test utilities and helpers
    • Test custom hooks
    • Test store actions
  2. Component Tests

    • Test component rendering
    • Test user interactions
    • Test error states
  3. Integration Tests

    • Test auth flows
    • Test API interactions
    • Test routing behavior

Build & Deployment

The webapp is deployed to Cloudflare Pages:

  1. Build Process

    • Vite builds optimized bundles
    • Assets are hashed for caching
    • Environment variables injected
  2. Deployment

    • Automatic deployment on push
    • Preview deployments for PRs
    • Production deployment on main branch

Environment Variables

Required environment variables:

  • VITE_API_URL - Backend API URL
  • VITE_APP_URL - Frontend app URL

Set in .env.local for development:

bash
VITE_API_URL=http://localhost:3333
VITE_APP_URL=http://localhost:5173

Common Tasks

Adding a New Route

  1. Create route file in /src/routes/
  2. Route is auto-discovered by TanStack Router
  3. Add navigation links as needed

Adding Authentication to a Route

  1. Place route in (auth) directory
  2. Route automatically requires authentication
  3. Unauthenticated users redirected to login

Managing Global State

  1. Define store slice in /src/stores/
  2. Use Zustand's create function
  3. Import and use in components

Troubleshooting

Route Not Found

  • Ensure route file is in /src/routes/
  • Check file naming conventions
  • Restart dev server if needed

Authentication Issues

  • Check session cookie in browser
  • Verify API endpoint is correct
  • Check network tab for failed requests

State Not Persisting

  • Verify store persistence config
  • Check browser storage limits
  • Clear storage and retry

Built with ❤️ by the Jubiloop team