Frontend Architecture
Next.js architecture principles and patterns
Frontend Architecture
Goal
Build a modern, performant Next.js 15 frontend with React 19 that prioritizes Server Components and minimal client-side state.
Technology Stack
- Next.js 15 with App Router
- React 19 with Server Components
- Tailwind CSS 3.4 for styling
- Shadcn/ui for accessible components
- react-hook-form + Zod 3.x for forms
Project Structure
app/
├── (auth)/ # Public routes (login, register)
├── (dashboard)/ # Protected routes
│ ├── employees/
│ ├── time-off/
│ ├── documents/
│ ├── team/
│ ├── goals/
│ └── settings/
├── layout.tsx
└── page.tsx
components/
├── ui/ # Shadcn components
├── forms/ # Form components
└── layout/ # Layout components
lib/
├── api.ts # API client
└── utils.ts # UtilitiesKey Principles
Server Components First
- Default to Server Components
- Use Client Components only when needed (interactivity, hooks)
- Fetch data on the server, pass to client as props
- Next.js 15 Compatibility: Ensure all route parameters (
params,searchParams) areawaited before use. This is a breaking change in Next.js 15.
State Management
| State Type | Solution | Example |
|---|---|---|
| Server data | TanStack Query + Server Components | Employee list, dashboard metrics |
| URL state | useSearchParams | Filters, pagination, search |
| Local state | useState | Modal open/close, form inputs |
| Global state | React Context (minimal) | Theme, user preferences |
No Redux/Zustand - TanStack Query, Server Components, and URL state cover most needs.
Server State with TanStack Query
For data that lives on the server and needs client-side interactivity, use TanStack Query (React Query v5):
// lib/queries/employees.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { api } from '@/lib/api'
// Fetch employees with caching
export function useEmployees(filters?: { departmentId?: string; status?: string }) {
return useQuery({
queryKey: ['employees', filters],
queryFn: () => api.employees.list(filters),
staleTime: 5 * 60 * 1000, // 5 minutes
})
}
// Fetch single employee
export function useEmployee(id: string) {
return useQuery({
queryKey: ['employees', id],
queryFn: () => api.employees.get(id),
enabled: !!id,
})
}
// Create employee with cache invalidation
export function useCreateEmployee() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: api.employees.create,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['employees'] })
},
})
}When to use TanStack Query:
- Data that multiple components need to share
- Data that needs to be refetched on focus/reconnect
- Optimistic updates (update UI before server confirms)
- Infinite scroll/pagination
- Real-time data synchronization
When to use Server Components instead:
- Initial page load data (SSR)
- Data that doesn't change during session
- Static content with infrequent updates
Data Visualization
- React Flow - Org chart visualization
- Recharts - Dashboard charts and graphs
- Use Server Components for initial data, hydrate on client for interactivity
Routing Patterns
- Route Groups:
(auth)for public,(dashboard)for protected - Dynamic Routes:
[id]for resource pages - Layouts: Shared UI (nav, sidebar) persists across pages
- Loading States:
loading.tsxfor suspense boundaries - Error Handling:
error.tsxfor error boundaries
Forms
- Use react-hook-form for form state
- Use Zod schemas for validation
- Validate on submit (not on change)
- Handle errors at field level
Pages to Build
| Page | Purpose |
|---|---|
| Dashboard | Overview metrics, pulse score, activity |
| Employees | List, profiles, org chart |
| Time-Off | Requests, calendar, balances |
| Documents | Upload, search, access control |
| Team Feed | Posts, comments, likes, AI summaries |
| Goals | Personal, team, company goals |
| Settings | Profile, tenant configuration |
| AI Chat | Assistant for policy questions |
Component Guidelines
Shadcn/ui Components
- Install as needed:
npx shadcn-ui@latest add button - Components are copied to project (fully customizable)
- Built on Radix UI primitives (accessible)
Component Organization
components/ui/- Base Shadcn componentscomponents/forms/- Form-specific componentscomponents/layout/- Navigation, sidebar, etc.
Performance
- Use Server Components for data fetching
- Lazy load heavy components
- Optimize images with Next.js Image
- Use Suspense for loading states
Accessibility
- Use semantic HTML
- Include ARIA labels
- Support keyboard navigation
- Test with screen readers
Implementation details to be defined during development