Client Area

Next.js App Router — Modern Patterns for 2026

ByDomain India Team·DomainIndia Engineering
6 min read24 Apr 20263 views
# Next.js App Router — Modern Patterns for 2026
TL;DR
Next.js App Router (introduced 2023) has replaced Pages Router as the default for new Next projects. This guide covers the patterns that matter for production — Server Components, Server Actions, streaming, partial prerendering, middleware, and deploying on DomainIndia VPS vs Vercel vs Cloudflare.
## App Router vs Pages Router — why change The old `pages/` directory approach was fine but bolted routing atop client-side React. App Router makes the server first-class:
FeaturePages RouterApp Router
Server/client boundarygetServerSideProps / getStaticProps at page levelPer-component via 'use client'
LayoutsManually composedNative layout files
Data fetchingHook-based mostlyfetch() in Server Components
StreamingLimitedBuilt-in via <Suspense>
FormsonSubmit handlerServer Actions (no fetch needed)
Metadata<Head> componentmetadata export + generateMetadata
For new projects in 2026: always App Router. Pages Router receives maintenance but no new features. ## Project layout ``` app/ ├── layout.tsx # root layout (wraps everything) ├── page.tsx # home page ├── loading.tsx # shown while page is loading ├── error.tsx # shown on errors ├── not-found.tsx # 404 ├── (marketing)/ # route group — doesn't add to URL │ ├── about/page.tsx │ └── pricing/page.tsx ├── (app)/ # route group for authenticated routes │ ├── layout.tsx # different layout (e.g. with sidebar) │ ├── dashboard/page.tsx │ └── settings/page.tsx ├── api/ # route handlers (former API routes) │ └── users/route.ts ├── blog/ │ ├── page.tsx # /blog │ └── [slug]/page.tsx # /blog/ ``` Parentheses `(group)` don't affect URL. Brackets `[slug]` = dynamic segment. ## Server Components by default Every component is server-rendered unless marked `'use client'`. **Server Component (default):** ```tsx // app/blog/page.tsx import { db } from '@/lib/db'; export default async function BlogList() { const posts = await db.post.findMany(); return (
    {posts.map(p =>
  • {p.title}
  • )}
); } ``` - Runs on server - Can directly query DB, use secrets - Ships zero JS to client for this component - Can't use `useState`, `useEffect`, browser APIs **Client Component:** ```tsx // app/components/LikeButton.tsx 'use client'; import { useState } from 'react'; export function LikeButton({ postId }: { postId: string }) { const [liked, setLiked] = useState(false); return ( setLiked(!liked)}> {liked ? '❤️' : '🤍'} ); } ``` - Runs on server (for SSR) AND client (for interactivity) - Needs `'use client'` directive at top - Use for anything interactive **Rule:** ship as much as Server Components; use Client Components only when needed. ## Data fetching Built-in `fetch` in Server Components with caching: ```tsx export default async function Page() { // Static — cached indefinitely const staticData = await fetch('https://api.example.com/static').then(r => r.json()); // Revalidate every 60s const dynamicData = await fetch('https://api.example.com/news', { next: { revalidate: 60 }, }).then(r => r.json()); // Always fresh const liveData = await fetch('https://api.example.com/stock', { cache: 'no-store', }).then(r => r.json()); return
...
; } ``` Next.js dedupes concurrent identical fetches per render. No extra query. ## Server Actions — mutations without fetch ```tsx // app/post/[id]/page.tsx import { revalidatePath } from 'next/cache'; async function addComment(postId: string, formData: FormData) { 'use server'; const text = formData.get('text') as string; await db.comment.create({ data: { postId, text } }); revalidatePath(`/post/${postId}`); } export default async function Post({ params }: { params: { id: string } }) { return ( Comment ); } ``` - Function executes on server - No fetch/API route needed - `revalidatePath` refreshes cached data - Progressive enhancement — works without JavaScript ## Streaming with Suspense Long-loading components don't block the page: ```tsx import { Suspense } from 'react'; export default function Dashboard() { return (
}> }>
); } ``` Header shows instantly; reports stream in when ready. Shell is fast; content fills in. ## Partial Prerendering (2026 stable) Combines static + dynamic in one route. Static parts ship from CDN; dynamic parts stream from origin. ```tsx // app/product/[id]/page.tsx export const experimental_ppr = true; import { Suspense } from 'react'; export default function Product({ params }) { return (
{/* Static — prerendered */} Loading cart...
}> {/* Dynamic — user-specific, rendered per request */} ); } ``` Best of SSG + SSR. ## Middleware — edge logic `middleware.ts` at project root — runs before every matched request: ```typescript import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { // Redirect non-www to www if (!request.nextUrl.host.startsWith('www.')) { return NextResponse.redirect(new URL(`https://www.${request.nextUrl.host}${request.nextUrl.pathname}`)); } // Auth gate if (request.nextUrl.pathname.startsWith('/dashboard')) { const token = request.cookies.get('session'); if (!token) { return NextResponse.redirect(new URL('/login', request.url)); } } // A/B test const bucket = request.cookies.get('ab')?.value || (Math.random() > 0.5 ? 'a' : 'b'); const response = NextResponse.next(); response.cookies.set('ab', bucket); return response; } export const config = { matcher: ['/((?!api|_next|favicon.ico).*)'], }; ``` Runs at the edge. Use for redirects, auth, A/B tests, geolocation routing. ## Metadata API (SEO) ```tsx // app/blog/[slug]/page.tsx import type { Metadata } from 'next'; export async function generateMetadata( { params }: { params: { slug: string } } ): Promise { const post = await getPost(params.slug); return { title: `${post.title} | Your Blog`, description: post.excerpt, openGraph: { title: post.title, description: post.excerpt, images: [post.coverImage], }, twitter: { card: 'summary_large_image' }, }; } ``` Next.js generates `` tags automatically. ## Static vs ISR vs SSR vs PPR ```tsx // Static (at build time) — default behaviour export default async function Page() { const data = await fetch('...'); return
{data}
; } // ISR (Incremental Static Regeneration) export const revalidate = 60; // seconds // SSR (every request fresh) export const dynamic = 'force-dynamic'; // PPR (partial, as above) export const experimental_ppr = true; ``` Pick based on data freshness:
Data freshnessStrategy
Never changes (docs, landing page)Static
Changes hourlyISR with revalidate: 3600
Per-user (dashboard)SSR
Mixed (user header + static product)PPR
## Deployment on DomainIndia **VPS (recommended for self-hosting):** ```bash npm run build # produces .next/ build output npm install --production node_modules/.bin/next start -p 3000 ``` systemd + nginx reverse proxy. See our [Go/Rust deployment articles](https://domainindia.com/support/kb/running-go-applications-vps-systemd-reverse-proxy) for pattern. **Shared cPanel:** Use `next build && next export` for static export (no dynamic features). Upload `out/` to public_html. For dynamic Next.js: needs Node.js app support on cPanel. Works for small apps. **Vercel:** simplest but vendor-locked and $$ at scale. **Cloudflare Workers:** runs Next.js via `@cloudflare/next-on-pages`. Good for global distribution. ## Common pitfalls ## FAQ
Q Pages Router or App Router for new projects?

App Router, unquestionably. Pages is legacy.

Q Next.js vs Remix vs Astro?

Next.js — most features, biggest ecosystem, enterprise-ready. Remix — similar features, smaller ecosystem, different philosophy. Astro — content-focused (blogs, docs), per-component framework choice. Pick Next for apps, Astro for content-heavy sites.

Q Can I host Next.js on shared hosting?

Static export yes. Dynamic rendering — needs Node.js app support. VPS is simpler for serious use.

Q Do Server Components replace APIs?

For internal data fetching — often yes. For mobile apps or third-party integrations, you still need REST/GraphQL APIs.

Q Server Actions vs traditional API routes?

Actions for forms + mutations triggered by user. API routes for webhooks, external consumers, GraphQL.

Deploy Next.js on a DomainIndia VPS — no vendor lock-in. Start with VPS

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket