Appearance
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
File-Based Routing
- Routes defined in
/src/routes/directory - Automatic code splitting per route
- Type-safe navigation and parameters
- Routes defined in
Authentication Flow
- Session-based auth with HTTP-only cookies
- Protected route groups:
(auth)vs(public) - Automatic redirects based on auth state
State Management Strategy
- Zustand for auth and global app state
- TanStack Query for server state caching
- Local React state for component UI
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 setupTanStack Router Configuration
Router Setup (main.tsx)
typescript
const router = createRouter({
routeTree,
context: {
...TanstackQuery.getContext(),
},
defaultPreload: 'intent',
scrollRestoration: true,
defaultStructuralSharing: true,
defaultPreloadStaleTime: 0,
})Route Structure
Root Route (
__root.tsx)- Handles authentication checks
- Provides layout wrapper
- Manages redirects
Public Routes (
(public)/)- Sign in/up pages
- Accessible without auth
- Auto-redirect if already authenticated
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
Session Management
- HTTP-only cookies for security
- Automatic session refresh
- Remember me functionality
Protected Routes
- Route-level auth checks
- Automatic redirects
- Preserved intended destination
Auth State
- Global auth store (Zustand)
- Persisted user data
- Loading states
Navigation
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 checkingPre-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
Routing
- Use file-based routing for organization
- Group related routes with folders
- Keep route components focused
State Management
- Use Zustand sparingly for global state
- Prefer server state with TanStack Query
- Local state for component-specific UI
Component Design
- Small, focused components
- Composition over complex components
- Proper TypeScript types
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
withCredentialsenabled 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.clientKey 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
Unit Tests
- Test utilities and helpers
- Test custom hooks
- Test store actions
Component Tests
- Test component rendering
- Test user interactions
- Test error states
Integration Tests
- Test auth flows
- Test API interactions
- Test routing behavior
Build & Deployment
The webapp is deployed to Cloudflare Pages:
Build Process
- Vite builds optimized bundles
- Assets are hashed for caching
- Environment variables injected
Deployment
- Automatic deployment on push
- Preview deployments for PRs
- Production deployment on main branch
Environment Variables
Required environment variables:
VITE_API_URL- Backend API URLVITE_APP_URL- Frontend app URL
Set in .env.local for development:
bash
VITE_API_URL=http://localhost:3333
VITE_APP_URL=http://localhost:5173Common Tasks
Adding a New Route
- Create route file in
/src/routes/ - Route is auto-discovered by TanStack Router
- Add navigation links as needed
Adding Authentication to a Route
- Place route in
(auth)directory - Route automatically requires authentication
- Unauthenticated users redirected to login
Managing Global State
- Define store slice in
/src/stores/ - Use Zustand's create function
- 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