System overview
The application follows a hybrid rendering strategy that combines:- Server-side rendering (SSR) for initial page loads and data fetching
- Client-side interactivity for dynamic features and real-time updates
- Static optimization where applicable for improved performance
Next.js App Router architecture
Contafy leverages Next.js 16’s App Router, which provides:File-based routing
Routes are defined by the folder structure in theapp/ directory:
page.tsx file represents a route endpoint that is automatically mapped to a URL.
Layout composition
Layouts provide shared UI across multiple pages:- Root layout (
app/layout.tsx): Wraps the entire application with providers and global UI - Route-specific layouts: Can be nested to provide section-specific UI
- Font loading (Geist Sans and Geist Mono)
- Global providers (ReactQueryProvider, TokenRefresher)
- Toast notifications (Sonner)
- Dark theme by default
Server vs Client components
Contafy follows a Server Components first approach:Server Components (default)
Server Components are the default in Next.js App Router and are used for:- Initial data fetching (see
app/dashboard/page.tsx:17-31) - Direct database/API access
- Rendering static content
- SEO-optimized pages
- Zero JavaScript sent to the client
- Direct access to backend resources
- Automatic code splitting
- Improved performance
app/dashboard/components/DashboardContent.tsx:42-57:
Client Components (‘use client’)
Client Components are used sparingly, only when needed for:- Interactive UI (forms, modals, dropdowns)
- Browser APIs (localStorage, window)
- React hooks (useState, useEffect, useQuery)
- Event handlers (onClick, onChange)
app/dashboard/expenses/components/ExpensesListContent.tsx:1-5:
Dynamic segments and search params
Contafy uses URL search parameters for filtering and pagination:State management
Server state with TanStack Query
TanStack Query (React Query) manages server state in Client Components:- Automatic caching: Reduces unnecessary API calls
- Background refetching: Keeps data fresh
- Optimistic updates: Immediate UI feedback
- Request deduplication: Prevents duplicate requests
components/providers/ReactQueryProvider.tsx:14-17:
Local state with React hooks
Simple component state uses standard React hooks:useStatefor local component stateuseEffectfor side effectsuseCallbackfor memoized callbacksuseMemofor computed values
URL state
URL search parameters serve as a source of truth for:- Current selected profile
- Date filters (month/year)
- Pagination state
- Search queries
- Shareable links
- Browser back/forward support
- Deep linking capability
Authentication architecture
Contafy uses JWT-based authentication with httpOnly cookies:Token storage
- Access token: Short-lived (stored in httpOnly cookie)
- Refresh token: Long-lived (stored in httpOnly cookie)
Token refresh mechanism
Thelib/api/client.ts:49-95 implements automatic token refresh:
- Client makes authenticated request
- If 401 received, automatically call refresh endpoint
- Retry original request with new token
- If refresh fails, redirect to login
Server vs Client authentication
Server Components (lib/api/server-client.ts):
- Read token from cookies directly
- Cannot refresh tokens (can’t modify cookies)
- Redirect to login on 401
lib/api/client.ts):
- Request token via API route
- Can refresh tokens automatically
- Handle 401 with retry logic
API client architecture
Contafy has separate API clients for server and client contexts:Server API client
Used in Server Components (lib/api/server-client.ts:37-86):
Client API client
Used in Client Components (lib/api/client.ts:97-186):
API proxy
Thenext.config.ts:5-12 configures a proxy to avoid CORS:
Component composition pattern
Contafy follows the Container/Presentational pattern:Page as orchestrator
Pages (page.tsx) act as “scriptwriters” with minimal logic:
Content components
Content components handle data fetching and composition:- Fetch data (Server Components) or manage queries (Client Components)
- Compose UI from smaller presentational components
- Handle loading and error states
Presentational components
Small, focused components inapp/[route]/components/:
- Accept data via props
- No data fetching
- Reusable and testable
- Pure presentation logic
Performance optimizations
Dynamic imports
Heavy components are lazy-loaded (app/dashboard/components/DashboardContent.tsx:17-32):
Parallel data fetching
Server Components fetch data in parallel withPromise.all():
Suspense boundaries
Suspense provides granular loading states:Error handling
Contafy implements error handling at multiple levels:- API level:
ApiErrorclass with status codes - Component level: Try-catch in async functions
- Route level:
error.tsxfor route-level errors - Global level:
app/error.tsxfor uncaught errors
Type safety
TypeScript strict mode ensures type safety throughout:- All API responses have defined types (
lib/types/) - Component props are fully typed
- No usage of
anytype - Path aliases configured (
@/*→ project root)