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-app
During 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 configuration
app
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.woff2
To 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 # /cart
Parallel 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_here
Access 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
vercel
Build your application:
npm run build
Start the production server:
npm run start
Conclusion #
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!