# Vite → Next.js Migration Record

This document records the completed migration from a Vite + React SPA to a Next.js fullstack application (initially Next.js 15; the repo is now on **Next.js 16** — see [Stack upgrades](#stack-upgrades-after-the-initial-migration) below).

## Migration Summary

| Aspect | Before (Vite SPA) | After (initial migration) |
|--------|--------------------|---------------------|
| Framework | Vite + React 19 | Next.js (App Router) |
| Routing | React Router DOM | File-system routing (`src/app/`) |
| Rendering | Client-side only | SSR + SSG (Server Components) |
| Backend | None (static data) | API routes + Prisma + PostgreSQL |
| Auth | None | NextAuth.js v5 (Credentials) |
| Images | Native `<img>` | `next/image` with optimization |
| Navigation | `<Link>` from react-router-dom | `<Link>` from next/link |
| CSS-in-JS | MUI + Emotion (client only) | MUI + Emotion + `AppRouterCacheProvider` (SSR) |
| SEO | None | Metadata API, JSON-LD, sitemap, robots.txt |
| Data | Hardcoded in components | PostgreSQL via Prisma (with static fallback) |
| Admin | None | Full CRUD panel at `/admin/*` |

## Files Deleted

### Vite infrastructure
- `vite.config.ts`
- `index.html` (Vite entry point)
- `src/main.tsx` (React root render)
- `src/App.tsx` (React Router setup)
- `src/App.css`
- `src/vite-env.d.ts`
- `tsconfig.app.json`
- `tsconfig.node.json`
- `eslint.config.js` (replaced with `eslint.config.mjs`)

### Old page components (replaced by App Router pages)
- `src/index.css` (merged into `globals.css`)
- `src/pages/Home/Home.tsx` → `src/app/page.tsx`
- `src/pages/Posts/Posts.tsx` → `src/app/posts/page.tsx` + `PostsClient.tsx`
- `src/pages/AboutUs/AboutUs.tsx` → `src/app/about/page.tsx` + `AboutContent.tsx`
- `src/pages/ContactUs/ContactUs.tsx` → `src/app/contact/page.tsx`
- `src/pages/Courses/Courses.tsx` → `src/app/courses/page.tsx`
- `src/components/common/PostCard/PostCard.tsx` (duplicate)
- `src/components/CourseCard/CourseCard.tsx` (duplicate, kept `common/CourseCard`)

## Files Created

### Next.js configuration
- `next.config.ts` — image remote patterns, optimization
- `eslint.config.mjs` — flat config with `eslint-config-next`
- `middleware.ts` — auth middleware for admin routes
- `.env.local` — environment variables (gitignored)
- `tsconfig.json` — unified TypeScript config

### App Router pages (27 routes)
- `src/app/layout.tsx` — root layout with ThemeRegistry, Banner, Footer, JSON-LD
- `src/app/page.tsx` — home page
- `src/app/globals.css` — global styles
- `src/app/about/page.tsx` + `AboutContent.tsx`
- `src/app/contact/page.tsx`
- `src/app/courses/page.tsx`
- `src/app/documentation/page.tsx`
- `src/app/help/page.tsx`
- `src/app/internship/page.tsx`
- `src/app/posts/page.tsx` + `PostsClient.tsx`
- `src/app/posts/[slug]/page.tsx` + `PostContent.tsx`
- `src/app/services/page.tsx` + `ServicesOverviewTabs.tsx`
- `src/app/services/training/page.tsx`
- `src/app/services/digital/page.tsx`
- `src/app/services/webdevelopment/page.tsx`
- `src/app/sitemap.ts`
- `src/app/robots.ts`

### Admin panel
- `src/app/admin/layout.tsx`
- `src/app/admin/page.tsx`
- `src/app/admin/login/page.tsx` + `LoginForm.tsx`
- `src/app/admin/posts/page.tsx` + `AdminPostsList.tsx`
- `src/app/admin/posts/new/page.tsx`
- `src/app/admin/posts/[id]/edit/page.tsx`
- `src/app/admin/courses/page.tsx` + `AdminCoursesList.tsx`
- `src/app/admin/courses/new/page.tsx`
- `src/app/admin/courses/[id]/edit/page.tsx`
- `src/app/admin/contacts/page.tsx` + `AdminContactsList.tsx`
- `src/components/admin/AdminSidebar.tsx`
- `src/components/admin/PostForm.tsx`
- `src/components/admin/CourseForm.tsx`

### API routes
- `src/app/api/auth/[...nextauth]/route.ts`
- `src/app/api/contact/route.ts`
- `src/app/api/posts/route.ts` + `[id]/route.ts`
- `src/app/api/courses/route.ts` + `[id]/route.ts`

### Backend infrastructure
- `prisma/schema.prisma`
- `prisma/seed.ts`
- `src/lib/prisma.ts`
- `src/lib/auth.config.ts` — Edge-safe NextAuth options shared with `middleware.ts` (no Prisma in the Edge bundle)
- `src/lib/auth.ts` — full NextAuth + Credentials provider (Prisma, bcrypt)
- `src/lib/utils.ts`
- `src/types/index.ts`

### Providers and SEO
- `src/components/providers/ThemeRegistry.tsx`
- `src/components/providers/AuthProvider.tsx`
- `src/components/JsonLd.tsx`

### Ported components
- `src/components/common/RichContent/RichContent.tsx`
- `src/components/common/RoutableTabs/RoutableTabs.tsx`
- `src/components/common/ArticleToc/ArticleTocSidebar.tsx`
- `src/components/common/ArticleToc/useArticleTocItems.ts`

## Components Modified (added "use client", updated imports)

| Component | Changes |
|-----------|---------|
| `Banner.tsx` | `"use client"`, react-router → next/link, img → next/image |
| `Footer.tsx` | `"use client"`, MUI Link → NextLink, added Documentation/Help links |
| `ImageSlider.tsx` | `"use client"`, img → next/image, public paths |
| `ServicesCards.tsx` | `"use client"`, img → next/image, public paths |
| `CourseCard.tsx` | `"use client"`, img → next/image, startDate type fix |
| `GradientBox.tsx` | `"use client"` (uses `styled`) |
| `SocialMediaCard.tsx` | `"use client"`, next/link for external URLs |
| `ContactForm.tsx` | `"use client"`, fetch `/api/contact` instead of setTimeout |
| `ITTraining.tsx` | next/link, icon correction |
| `DigitalMarketing.tsx` | next/link |
| `WebDevelopment.tsx` | next/link |
| `Internship.tsx` | next/link |

## Build Errors Fixed During Migration

1. **`styled` in Server Component:** `AboutSection = styled(Box)` used in server page → extracted to `AboutContent.tsx` client component.
2. **`useSearchParams` Suspense:** `/admin/login` → wrapped `LoginForm` in `<Suspense>` boundary.
3. **`params` nullable:** `useParams<{ id: string }>()` + null check in edit pages.
4. **Type assertion:** NextAuth session callback → `(session.user as unknown as { role: string }).role`.
5. **`pathname` nullable:** AdminSidebar → `pathname?.startsWith()` optional chaining.
6. **ESLint config:** `@eslint/flatcompat` doesn't exist → `FlatCompat` from `@eslint/eslintrc` + `fixupConfigRules` from `@eslint/compat`.
7. **Missing types:** `@types/prismjs` needed for RichContent.
8. **`searchParams` nullable:** RoutableTabs → `searchParams?.get()` optional chaining for App Router (nullable `useSearchParams`).

## Stack upgrades (after the initial migration)

The following changes landed after the Vite → Next.js migration and are reflected in `package.json` and the app source:

| Change | Notes |
|--------|--------|
| **Next.js 16** | Upgraded from 15.x. Production `next build` uses **Turbopack** by default; use `next build --webpack` if you need the webpack compiler. |
| **`@mui/material-nextjs` v7** | Declares compatibility with Next.js 16. `ThemeRegistry` imports `AppRouterCacheProvider` from `@mui/material-nextjs/v16-appRouter`. Core MUI packages remain **v6** (`@mui/material`, `@mui/icons-material`). |
| **Home page split** | `src/app/page.tsx` exports `metadata` and renders `<HomePageContent />`. `HomePageContent.tsx` is a **Client Component** so MUI `Button` can use `component={next/link}` without RSC serialization errors. |
| **Feature sections** | `Internship`, `ITTraining`, `DigitalMarketing`, and `WebDevelopment` include `"use client"` for the same MUI + `next/link` pattern. |
| **`/documentation` page** | Contact CTA uses a plain `next/link` instead of `Box component={Link}` to avoid passing a function component from a Server Component during static generation. |
| **Edge-safe auth middleware** | `middleware.ts` uses `NextAuth(authConfig)` from `auth.config.ts` instead of importing `auth` from `auth.ts`, so Prisma/bcrypt are not bundled into the Edge Function — required for Vercel Hobby’s ~1 MB `_middleware` limit. |
| **Company branding** | Public name **Evensiva Technologies** in metadata and JSON-LD; `Banner` and `Footer` use a two-line lockup (**Evensiva** + **TECHNOLOGIES**) next to `SiteLogo`. |
| **Layout shell & UI spacing** | Root `layout.tsx`: flex column `body` + `<main sx={{ flex: 1 }}>` so the footer stays at the bottom on short pages. Marketing pages: consistent section padding, typography scale (e.g. heading letter-spacing), card/button motion (easing + hover lift). `/services` uses `pt` only on the outer box to avoid doubled padding with tab content. Contact route renders `ContactForm` without an extra wrapper `div`. |
