The Complete Guide to Next.js Project Setup in 2025
A step-by-step guide to setting up a Next.js project in 2025.

Setting up a Next.js project in 2025 has never been more straightforward, yet the options and configurations available can be overwhelming for newcomers. This guide provides clear, actionable steps to create a production-ready Next.js application with modern best practices.
Why Choose Next.js in 2025?#
Before diving into the setup process, let's quickly review why Next.js remains a top choice for developers:
• Unified Development Experience: Server and client components in one codebase
• Performance Optimization: Automatic code splitting, image optimization, and font optimization
• Flexible Rendering Options: Server-side rendering, static site generation, and client-side rendering
• Built-in Routing: File-system based routing with support for layouts, loading states, and error boundaries
• Developer Experience: Fast refresh, TypeScript support, ESLint integration, and more
Setting Up a Next.js Project Automatic Installation (Recommended)The easiest way to start a new Next.js project is using create-next-app, which sets up everything automatically. Here's how to do it:
npx create-next-app@latest my-nextjs-appDuring installation, you'll be prompted with several configuration options:
What is your project named? my-nextjs-app
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 to use `src/` directory? Yes
Would you like to use App Router? (recommended) Yes
Would you like to customize the default import alias (@/<em>)? Yes
What import alias would you like configured? @/</em>Let's break down these options:
• TypeScript: Provides type safety and better developer experience
• ESLint: Helps maintain code quality and consistency
• Tailwind CSS: A utility-first CSS framework for rapid UI development
• src/ directory: Separates your application code from configuration files
• App Router: The recommended routing system (vs. the older Pages Router)
• Import alias: Creates a shorthand for imports (e.g., @/components/Button instead of ../../components/Button)
If you prefer more control, you can set up a Next.js project manually:
- Create a new project directory and initialize a package.json file:
mkdir my-nextjs-app
cd my-nextjs-app
npm init -y- Install the required dependencies:
npm install next@latest react@latest react-dom@latest- Open your package.json file and add the following scripts:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
}- Create the basic folder structure:
mkdir -p app public- Create the essential files:
// app/layout.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
// app/page.js
export default function Home() {
return (
<main>
<h1>Hello, Next.js!</h1>
</main>
);
}Understanding the Project Structure#
After setting up your Next.js project, you'll have a directory structure that looks something like this:
my-nextjs-app/
├── app/ # App Router directory
│ ├── layout.js # Root layout (required)
│ └── page.js # Home page
├── public/ # Static assets
├── src/ # Optional source directory
├── .eslintrc.json # ESLint configuration
├── next.config.js # Next.js configuration
├── package.json # Project dependencies and scripts
└── tsconfig.json # TypeScript configurationapp Directory
The App Router uses a file-system based router where folders define routes. Here's how it works:
app/
├── layout.js # Root layout (applied to all pages)
├── page.js # Home page (/)
├── about/
│ └── page.js # About page (/about)
├── blog/
│ ├── layout.js # Blog layout (applied to all blog pages)
│ ├── page.js # Blog index page (/blog)
│ └── [slug]/ # Dynamic route segment
│ └── page.js # Individual blog post page (/blog/post-1)Special files in the App Router:
• layout.js: Shared UI for a segment and its children
• page.js: UI for a route and makes it publicly accessible
• loading.js: Loading UI for a segment and its children
• error.js: Error UI for a segment and its children
• not-found.js: UI for 404 errors
public Directory
The public directory is used for static assets like images, fonts, and other files that don't need to be processed by Next.js. These files are served from the base URL of your application.
public/
├── favicon.ico
├── images/
│ └── hero.jpg
└── fonts/
└── inter.woff2To use these assets in your application:
// Image from public directory
<img src="/images/hero.jpg" alt="Hero image" />
// Font from public directory
<style jsx global>{`
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
}
`}</style>• next.config.js: Configure Next.js behavior
• .eslintrc.json: ESLint rules
• tsconfig.json: TypeScript configuration
• .env.local: Environment variables
Creating Your First Pages and Routes#
Let's create a few pages to understand how routing works in Next.js:
1. Home Page (Already Created)// app/page.js
export default function Home() {
return (
<div>
<h1>Welcome to My Next.js App</h1>
<p>This is the home page.</p>
</div>
);
}// app/about/page.js
export default function About() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our company.</p>
</div>
);
}// app/blog/page.js
export default function Blog() {
return (
<div>
<h1>Blog</h1>
<ul>
<li>
<a href="/blog/first-post">First Post</a>
</li>
<li>
<a href="/blog/second-post">Second Post</a>
</li>
</ul>
</div>
);
}// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
return (
<div>
<h1>Blog Post: {params.slug}</h1>
<p>This is the content of the blog post.</p>
</div>
);
}Advanced Routing Features#
Next.js App Router provides several advanced routing features:
1. LayoutsLayouts allow you to share UI between multiple pages. The root layout is required and must contain html and body tags:
// app/layout.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/blog">Blog</a>
</nav>
</header>
<main>{children}</main>
<footer> 2025 My Next.js App</footer>
</body>
</html>
);
}You can also create nested layouts:
// app/blog/layout.js
export default function BlogLayout({ children }) {
return (
<div>
<h1>Blog</h1>
<div className="blog-container">
<div className="content">{children}</div>
<aside className="sidebar">
<h2>Recent Posts</h2>
{/<em> Sidebar content </em>/}
</aside>
</div>
</div>
);
}Create loading UI that displays while page content is loading:
// app/blog/loading.js
export default function Loading() {
return <div className="loading-spinner">Loading...</div>;
}Create error UI for specific segments:
// app/blog/[slug]/error.js
'use client'; // Error components must be Client Components
import { useEffect } from 'react';
export default function Error({ error, reset }) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error);
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}Route groups allow you to organize routes without affecting the URL structure:
app/
├── (marketing)/ # Route group (doesn't affect URL)
│ ├── about/
│ │ └── page.js # /about
│ └── contact/
│ └── page.js # /contact
└── (shop)/ # Another route group
├── products/
│ └── page.js # /products
└── cart/
└── page.js # /cartParallel routes allow you to simultaneously show multiple pages in the same view:
app/
├── dashboard/
│ ├── layout.js # Layout that defines slots
│ ├── page.js # Main dashboard content
│ ├── @stats/ # Stats slot
│ │ └── page.js # /dashboard (stats section)
│ └── @activity/ # Activity slot
│ └── page.js # /dashboard (activity section)// app/dashboard/layout.js
export default function DashboardLayout({ children, stats, activity }) {
return (
<div className="dashboard">
<div className="main">{children}</div>
<div className="stats-panel">{stats}</div>
<div className="activity-feed">{activity}</div>
</div>
);
}Data Fetching#
Next.js provides multiple ways to fetch data:
1. Server Components (Default)// app/users/page.js
async function getUsers() {
const res = await fetch('https://api.example.com/users');
return res.json();
}
export default async function UsersPage() {
const users = await getUsers();
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}// app/counter/page.js
'use client';
import { useState, useEffect } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
const res = await fetch('/api/data');
const json = await res.json();
setData(json);
}
fetchData();
}, []);
return (
<div>
<h1>Counter: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
{data && <div>Data: {JSON.stringify(data)}</div>}
</div>
);
}// app/api/hello/route.js
export async function GET() {
return Response.json({ message: 'Hello, Next.js!' });
}
export async function POST(request) {
const body = await request.json();
return Response.json({
message: 'Data received',
data: body,
});
}Styling Your Next.js Application#
Next.js supports various styling methods:
1. Global CSS// app/globals.css
body {
font-family: 'Inter', sans-serif;
margin: 0;
padding: 0;
color: #333;
}
h1 {
font-size: 2.5rem;
}
/<em> Import in your root layout </em>/
// app/layout.js
import './globals.css';// app/components/Button.module.css
.button {
padding: 0.5rem 1rem;
background-color: #0070f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.button:hover {
background-color: #0051a2;
}
// app/components/Button.js
import styles from './Button.module.css';
export default function Button({ children }) {
return (
<button className={styles.button}>
{children}
</button>
);
}If you selected Tailwind CSS during setup:
// app/page.js
export default function Home() {
return (
<div className="p-6 max-w-4xl mx-auto">
<h1 className="text-3xl font-bold text-blue-600">
Welcome to My Next.js App
</h1>
<p className="mt-4 text-gray-700">
This page is styled with Tailwind CSS.
</p>
<button className="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Get Started
</button>
</div>
);
}Optimizing Images and Fonts#
Next.js provides built-in components for optimizing images and fonts:
1. Image Optimization// app/gallery/page.js
import Image from 'next/image';
export default function Gallery() {
return (
<div>
<h1>Image Gallery</h1>
<div className="grid grid-cols-2 gap-4">
<Image
src="/images/photo1.jpg"
alt="Photo 1"
width={500}
height={300}
priority
/>
<Image
src="/images/photo2.jpg"
alt="Photo 2"
width={500}
height={300}
/>
</div>
</div>
);
}// app/layout.js
import { Inter, Roboto_Mono } from 'next/font/google';
// Load Inter font for general text
const inter = Inter({
subsets: ['latin'],
display: 'swap',
});
// Load Roboto Mono for code blocks
const robotoMono = Roboto_Mono({
subsets: ['latin'],
display: 'swap',
});
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>
<main>{children}</main>
<pre className={robotoMono.className}>
{`function hello() {
console.log('Hello, Next.js!');
}`}
</pre>
</body>
</html>
);
}Environment Variables and Configuration#
1. Environment VariablesCreate a .env.local file in your project root:
# .env.local
DATABASE_URL=postgres://user:password@localhost:5432/mydb
API_KEY=your_api_key_hereAccess environment variables in your code:
// Server Component (secure, not exposed to the client)
export default async function Page() {
const apiKey = process.env.API_KEY;
// Use apiKey to fetch data
}
// For client-side code, prefix with NEXT_PUBLIC_
// .env.local
NEXT_PUBLIC_ANALYTICS_ID=UA-123456789-1
// Client Component
'use client';
export default function AnalyticsComponent() {
const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID;
// Use analyticsId
}Customize Next.js behavior with next.config.js:
// next.config.js
/*<em> @type {import('next').NextConfig} </em>/
const nextConfig = {
// Enable React strict mode
reactStrictMode: true,
// Configure image domains for next/image
images: {
domains: ['example.com', 'cdn.example.com'],
},
// Add redirects
async redirects() {
return [
{
source: '/old-blog/:slug',
destination: '/blog/:slug',
permanent: true,
},
];
},
// Add headers
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY',
},
],
},
];
},
};
module.exports = nextConfig;Deploying Your Next.js Application#
Next.js applications can be deployed to various platforms:
1. Vercel (Recommended)# Install Vercel CLI
npm install -g vercel
# Deploy
vercelBuild your application:
npm run buildStart the production server:
npm run startConclusion#
Setting up a Next.js project in 2025 has never been easier. With the App Router, server components, and built-in optimizations, Next.js provides a powerful foundation for building modern web applications.
By following this guide, you've learned how to:
• Set up a Next.js project using automatic or manual installation
• Understand the project structure and routing system
• Create pages, layouts, and handle loading and error states
• Implement data fetching strategies
• Style your application with various methods
• Optimize images and fonts
• Configure environment variables and Next.js settings
• Deploy your application
Next.js continues to evolve with each release, so be sure to check the official documentation for the latest features and best practices.
Happy coding!


