Розробка CSR (Client-Side Rendering) для веб-застосунку
Client-Side Rendering — рендер інтерфейсу повністю у браузері. Сервер відправляє мінімальний HTML-скелет та JS-бандл, браузер виконує код і будує DOM. Це архітектура SPA (Single Page Application): навігація без перезавантаження сторінки, багаті інтерактивні інтерфейси, стан живе в пам'яті клієнта.
CSR — правильний вибір для закритих застосунків: дашборди, кабінети, інструменти, платформи. Де SEO не потрібен, але потрібен гарячий інтерфейс з локальним станом.
Коли CSR краще за SSR
| Критерій | CSR | SSR |
|---|---|---|
| SEO для публічного контенту | Погано | Добре |
| Перша загрузка | Повільніше | Швидше |
| Наступні переходи | Миттєво | Кожен раз запит |
| Складне стан на клієнті | Природно | Ускладнюється |
| Серверні ресурси | Браузер робить роботу | Потрібен сервер для рендеру |
| Оффлайн-режим (PWA) | Можливо | Складно |
Архітектура React SPA з Vite
src/
api/ # HTTP-клієнти та типи
components/ # Переіспользуємі компоненти
features/ # Feature-модулі (auth, dashboard, reports)
auth/
components/
hooks/
store.ts
hooks/ # Глобальні хуки
lib/ # Утиліти, конфігурації
pages/ # Сторінки = маршрути
router/ # Налаштування TanStack Router
stores/ # Zustand stores
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
export default defineConfig({
plugins: [TanStackRouterVite(), react()],
resolve: { alias: { '@': '/src' } },
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['@tanstack/react-router'],
query: ['@tanstack/react-query'],
},
},
},
},
});
Роутинг з TanStack Router
// src/router/routes.ts
import { createRootRoute, createRoute, createRouter } from '@tanstack/react-router';
import { RootLayout } from '@/components/layouts/RootLayout';
const rootRoute = createRootRoute({ component: RootLayout });
const dashboardRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/dashboard',
component: lazy(() => import('@/pages/Dashboard')),
beforeLoad: ({ context }) => {
if (!context.auth.isAuthenticated) {
throw redirect({ to: '/login' });
}
},
});
export const router = createRouter({
routeTree: rootRoute.addChildren([dashboardRoute, ...]),
context: { auth: undefined! },
});
TanStack Router — типобезопасний роутинг: параметри маршрутів, search-параметри та контекст типізовані на рівні TypeScript.
Управління серверним станом
TanStack Query для всіх асинхронних даних:
// features/products/hooks/useProducts.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { productsApi } from '@/api/products';
export function useProducts(filters: ProductFilters) {
return useQuery({
queryKey: ['products', filters],
queryFn: () => productsApi.getList(filters),
staleTime: 5 * 60 * 1000, // 5 хвилин до повторного запиту
placeholderData: (prev) => prev, // Не видаляти старі дані при змінні фільтрів
});
}
export function useUpdateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: productsApi.update,
onSuccess: (updated) => {
// Оновити конкретний елемент у кешу без рефетчу
queryClient.setQueryData(
['products', updated.id],
updated
);
// Інвалідувати список
queryClient.invalidateQueries({ queryKey: ['products'] });
},
});
}
Оптимізація бандла
CSR-застосунки страждають від великого initial bundle. Стратегії:
Code splitting за маршрутами:
const DashboardPage = lazy(() => import('@/pages/Dashboard'));
const ReportsPage = lazy(() => import('@/pages/Reports'));
Аналіз бандла:
npx vite-bundle-analyzer
Динамічний імпорт важких бібліотек:
async function exportToExcel(data: Row[]) {
// xlsx завантажується тільки при кліку на "Експорт"
const { utils, writeFile } = await import('xlsx');
const ws = utils.json_to_sheet(data);
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, 'Дані');
writeFile(wb, 'export.xlsx');
}
PWA та оффлайн-режим
// vite.config.ts — Vite PWA plugin
import { VitePWA } from 'vite-plugin-pwa';
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\/products/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'api-products',
expiration: { maxAgeSeconds: 24 * 60 * 60 },
},
},
],
},
})
Строки реалізації
- Тиждень 1: Vite + TypeScript + Router, аутентифікація (JWT/OAuth), структура проекту
- Тиждень 2–3: основні сторінки, TanStack Query для API, форми (React Hook Form + Zod)
- Тиждень 4: оптимізація бандла, code splitting, тестування (Vitest + Testing Library)
Для закритих SPA-застосунків з десятками маршрутів та складною логікою CSR залишається найбільш продуктивною архітектурою — без обмежень SSR, повний контроль над станом.







