Skip to content

State and Data Fetching

WhatWhere it lives
Auth token and user roleAuthContext (localStorage + React context)
Theme (dark/light)ThemeContext
API responses and server stateTanStack React Query
Form stateReact Hook Form (local to each form component)

The QueryClient is configured at the root level in App.tsx with these defaults:

const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false,
retry: 1,
},
},
});

All API requests use the VITE_API_BASE_URL env var as the base URL. The JWT token from AuthContext is added as a header:

const { token } = useAuth();
const { data } = useQuery({
queryKey: ["articles", page],
queryFn: () =>
fetch(`${import.meta.env.VITE_API_BASE_URL}/article?page=${page}`, {
headers: { Authorization: `Bearer ${token}` },
}).then((r) => r.json()),
});

Pages are lazily imported to reduce initial bundle size:

const ArticlesPage = lazy(() => import("./pages/Articles"));

A Suspense boundary at the router level shows a loading skeleton while the page chunk loads.