Frontend Development: React, Next.js, Vue, Nuxt, TypeScript
Bundle grew to 3.1 MB gzip — real number from project that came to us for audit. Reason: moment.js (72 KB) pulled locales for all 160 languages, lodash imported whole instead of tree-shaking, three component libraries loaded simultaneously. TTFB good, but TTI (Time to Interactive) on mobile — 14 seconds. Users left.
Frontend is not "draw pretty." It's performance, typing, rendering strategy, bundle management, and multi-year maintainability.
React and Next.js: When and Why App Router
React is our primary UI framework for complex interfaces. Next.js is standard choice for projects with SEO requirements or SSR.
Pages Router vs App Router — actual question for new Next.js 13+ projects. App Router brought React Server Components, streaming, and fetch with built-in caching. Real advantages: catalog page with thousands of products renders on server without shipping filtering logic to client, JS bundle smaller.
But App Router is different mindset. "use client" must be set consciously. Real mistake: developer marks entire layout as "use client" for one state and loses all RSC benefits. Rule: keep Server Components as high in tree as possible, "use client" — only on interactive leaf components.
ISR (Incremental Static Regeneration) — powerful tool for content sites. Page generated statically and cached. After revalidate seconds pass, Next.js regenerates page in background — user always gets cached version, never waits for SSR. On 50,000 page catalog with ISR and CDN — TTFB < 50ms for any page.
Vue 3 and Nuxt 3: Composition API and SSR
Vue 3 with Composition API — different development style vs Options API, closer to React Hooks. setup() and <script setup> syntax makes code more reusable via composables.
Nuxt 3 — Vue framework with SSR/SSG like Next.js. useAsyncData and useFetch — built-in composables for data fetching with deduplication and hydration. Auto-imports of components and composables — convenient feature, but can confuse debugging: where did this function come from?
Nuxt Content — module for working with Markdown/MDX files as content. Convenient for docs, blogs, content sites.
Hydration mismatch — specific Vue and React SSR pain. If server content differs from client render (date/time, random IDs, browser-specific data) — warning in console, in production — visual artifacts. Solution: <ClientOnly> component for browser content, suppressHydrationWarning for dynamic timestamps.
TypeScript: Not Optional Feature
TypeScript mandatory on any project planned for support over 3 months or team bigger than one developer. Argument "write fast without types" works only first 2 weeks.
Concrete benefit: refactor API response — change type in one place, TypeScript shows all places needing adaptation. Without types — production bug in a week.
strict: true in tsconfig.json — mandatory. noImplicitAny, strictNullChecks, strictFunctionTypes. Pain from Type 'undefined' is not assignable in development worth less than Cannot read properties of undefined in production.
tRPC — end-to-end typing from backend (Node.js) to frontend without separate schema (unlike GraphQL). Change backend procedure return type — TypeScript immediately shows errors on frontend. Overhead: Node.js backend only, for Laravel need other approaches (OpenAPI → Zod schema generation).
Performance: Concrete Metrics and Tools
Bundle analysis — starting point. @next/bundle-analyzer or rollup-plugin-visualizer (for Vite) — run before each major deploy. Goal: no page requires > 200 KB JS gzip for first paint.
Dynamic imports for heavy components:
const RichEditor = dynamic(() => import('@/components/RichEditor'), {
ssr: false,
loading: () => <EditorSkeleton />,
});
Editors (Tiptap, Quill, CodeMirror) — typical dynamic import candidates. Without, they end up in main bundle.
React DevTools Profiler — find unnecessary re-renders. React.memo, useMemo, useCallback — not silver bullet, but pointwise tools. Premature memoization of everything adds overhead without benefit. Profile first, optimize then.
Virtualization for long lists. 10,000 row list in DOM — guaranteed freeze. @tanstack/virtual or react-window render only visible items. 50,000 row table: with virtualization — 60fps, without — browser hangs on scroll.
State Management: Without Overengineering
For most applications enough:
- React Query / TanStack Query — for server state (API data, caching, invalidation)
- Zustand — for global client state (lightweight, no Redux boilerplate)
- React Hook Form — for forms
Redux Toolkit justified for very complex global state with many interactions. Most tasks — overkill.
Recoil, Jotai — atomic approaches, work well for independent state pieces.
CSS and Design System
Tailwind CSS 4 — standard choice for new projects. Utility-first, excellent component library integration (Radix UI, Headless UI), PostCSS pipeline.
CSS Modules — alternative when need more explicit style isolation. Works well in Next.js out-of-box.
Radix UI + Tailwind (Shadcn/ui pattern) — headless components with full style control. No dependency lock-in: components copied to project and fully customized.
Storybook — for documenting component library. Especially useful in teams where designer checks components independently from pages.
Testing
| Level | Tool | What We Test |
|---|---|---|
| Unit | Vitest | Utilities, hooks, pure functions |
| Component | Testing Library | Render, interactions |
| E2E | Playwright | Critical user flows |
| Visual | Chromatic (Storybook) | UI regression |
E2E tests via Playwright — for checkout, authentication, critical forms. Not everything: big e2e suite maintenance is expensive.
Timeline Guidelines
| Task | Timeline |
|---|---|
| SPA (dashboard, CRM interface) | 8–16 weeks |
| Next.js site with SSR/ISR | 6–14 weeks |
| Frontend for existing API | 4–10 weeks |
| Component library | 6–12 weeks |
Cost calculated after components, screens, and API integrations decomposition.







