Next.js is the most popular framework for building web applications with React. It takes care of routing, page rendering, image optimisation, and deployment — so you can focus on building your product rather than configuring infrastructure.
This is the website framework behind ExplainX, Vercel's own site, and thousands of production apps. If you want to build real websites and web apps in 2026, Next.js is the foundation worth learning.
What Next.js adds over plain HTML/CSS
| Plain HTML/CSS | Next.js |
|---|---|
| One file = one page | Automatic routing from folder structure |
Manual <script> includes | Component system with imports |
| No built-in data fetching | async server components fetch data directly |
| Static files only | Mix of static pages, server-rendered pages, and APIs |
| Manual image handling | <Image> component with auto-optimisation |
| Deploy manually | git push → deploy to Vercel automatically |
Prerequisites
You need Node.js installed. Check:
node --version
If you get command not found, follow the Node.js install guide first. You need Node.js 18.17 or higher for Next.js 15.
Step 1: Create your first Next.js project
Next.js provides a CLI tool called create-next-app that scaffolds everything for you. Run:
npx create-next-app@latest my-nextjs-app
You'll be asked several questions. For a beginner setup, answer like this:
Would you like to use TypeScript? → Yes
Would you like to use ESLint? → Yes
Would you like to use Tailwind CSS? → Yes
Would you like your code inside a `src/` directory? → No
Would you like to use App Router? → Yes
Would you like to use Turbopack for next dev? → Yes
Would you like to customise the import alias? → No
This creates a folder called my-nextjs-app with everything configured.
Navigate into it:
cd my-nextjs-app
Step 2: Understand the project structure
my-nextjs-app/
├── app/ ← All your pages and layouts live here
│ ├── layout.tsx ← Root layout — wraps every page
│ ├── page.tsx ← Your homepage (/)
│ └── globals.css ← Global styles
├── public/ ← Static files (images, fonts, etc.)
├── next.config.ts ← Next.js configuration
├── package.json ← Dependencies and scripts
├── tailwind.config.ts ← Tailwind CSS configuration
└── tsconfig.json ← TypeScript configuration
The app/ directory is the most important. Every folder inside app/ becomes a route in your app:
app/page.tsx → /
app/about/page.tsx → /about
app/blog/page.tsx → /blog
app/blog/[slug]/page.tsx → /blog/any-slug-here
Step 3: Start the development server
npm run dev
Open your browser to http://localhost:3000. You'll see the default Next.js welcome page.
Leave this running. Every time you save a file, the page updates in the browser automatically — this is called hot reload.
Step 4: Edit the homepage
Open app/page.tsx in your editor. You'll see the default starter code. Delete everything inside the file and replace it with:
export default function HomePage() {
return (
<main className="min-h-screen flex flex-col items-center justify-center p-8">
<h1 className="text-4xl font-bold mb-4">Hello, Next.js</h1>
<p className="text-gray-500 text-lg">My first Next.js page.</p>
</main>
);
}
Save the file. Your browser updates instantly.
What's happening here:
export default function HomePage()— everypage.tsxmust export a default function- The function returns JSX — HTML-like syntax with JavaScript inside curly braces
- The
classNameattributes are Tailwind CSS utility classes
Step 5: Create a new page
Add an About page. Create a new folder and file:
mkdir app/about
Create app/about/page.tsx:
export default function AboutPage() {
return (
<main className="min-h-screen p-8 max-w-2xl mx-auto">
<h1 className="text-3xl font-bold mb-6">About</h1>
<p className="text-gray-600 leading-relaxed">
This is my first Next.js project. I built it by following a beginner guide.
</p>
</main>
);
}
Go to http://localhost:3000/about — the page is live. No configuration needed. The folder name becomes the URL.
Step 6: Add a shared layout
The app/layout.tsx file wraps every page in your app. This is where you put things that appear on every page — navigation, footers, font loading, meta tags.
Open app/layout.tsx. Add a simple nav:
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "My Next.js App",
description: "My first Next.js project",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<nav className="border-b px-8 py-4 flex gap-6">
<a href="/" className="font-semibold hover:text-blue-600">Home</a>
<a href="/about" className="font-semibold hover:text-blue-600">About</a>
</nav>
{children}
</body>
</html>
);
}
Now every page has the nav bar — and you only wrote it once.
Step 7: Server vs Client components
This is the most important concept in the App Router.
Server components (the default) — render on the server. Can fetch data directly. Cannot use browser APIs (window, document) or React hooks (useState, useEffect).
Client components — render in the browser. Can use browser APIs and React hooks. Add "use client" at the top of the file.
Rule of thumb: Start with server components (the default). Only add "use client" when you need interactivity or browser APIs.
Example of a client component — a counter button:
Create app/components/Counter.tsx:
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button
onClick={() => setCount(count + 1)}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Clicked {count} times
</button>
);
}
Now use it in your homepage (a server component):
import Counter from "./components/Counter";
export default function HomePage() {
return (
<main className="min-h-screen flex flex-col items-center justify-center p-8 gap-6">
<h1 className="text-4xl font-bold">Hello, Next.js</h1>
<Counter />
</main>
);
}
The homepage stays a server component. The Counter is a client component embedded inside it. This is the standard pattern.
Step 8: Fetch data in a server component
Server components can async/await directly — no useEffect needed:
Create app/posts/page.tsx:
async function getPosts() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return (
<main className="p-8 max-w-2xl mx-auto">
<h1 className="text-3xl font-bold mb-6">Posts</h1>
<ul className="space-y-4">
{posts.map((post: { id: number; title: string; body: string }) => (
<li key={post.id} className="border rounded p-4">
<h2 className="font-semibold mb-1">{post.title}</h2>
<p className="text-gray-500 text-sm">{post.body}</p>
</li>
))}
</ul>
</main>
);
}
Visit http://localhost:3000/posts. It fetches and displays real data — no loading state, no client-side JavaScript fetch. The data is fetched on the server and the HTML arrives ready.
Step 9: Dynamic routes
Create a route that accepts a variable segment — like a blog post URL.
Create app/posts/[id]/page.tsx:
async function getPost(id: string) {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
return res.json();
}
export default async function PostPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const post = await getPost(id);
return (
<main className="p-8 max-w-2xl mx-auto">
<h1 className="text-2xl font-bold mb-4">{post.title}</h1>
<p className="text-gray-600">{post.body}</p>
</main>
);
}
Visit http://localhost:3000/posts/1, then /posts/2, then /posts/5. Same file, different data — the [id] folder makes the segment dynamic.
Useful scripts
npm run dev # Start development server (with hot reload)
npm run build # Build for production
npm run start # Start the production server
npm run lint # Check for code issues
Deploy to Vercel in 2 minutes
Vercel is made by the same team that makes Next.js. Deploying takes two steps:
- Push your project to GitHub (see the Git guide)
- Go to vercel.com, click Add New Project, import your GitHub repo
Vercel detects Next.js automatically. Every git push deploys a new version. Your site gets a free .vercel.app URL instantly.
What to build next
- Add a contact form using a Next.js API route (
app/api/contact/route.ts) - Connect a database using Prisma + PostgreSQL (or Supabase for a hosted option)
- Add authentication with NextAuth.js or Clerk
- Deploy to a custom domain on Vercel
ExplainX is built with Next.js, Prisma, and Tailwind — the same stack you just set up.