Every Next.js application starts the same way — a single command that scaffolds a complete, working project in seconds. But what that command produces, what the resulting files mean, and what decisions get made during setup are worth understanding clearly. A project that starts well-configured is significantly easier to work with than one where foundational choices were made hastily or without understanding what they entail.
Prerequisites
Before creating a Next.js application, Node.js must be installed. Next.js requires a minimum of Node.js 20.9, which is the current LTS release. Any version at or above this minimum will work. Node.js installation is covered in the Node.js research series.
To verify what is currently installed:
node --version
Creating a New Project
The standard way to create a Next.js application is with create-next-app — an officially maintained CLI tool that scaffolds a complete project with all the necessary dependencies and configuration in place.
npx create-next-app@latest my-app
Replace my-app with whatever the project should be called. The @latest tag ensures the most recent stable version of Next.js is used rather than a cached older version.
Running this command starts an interactive prompt:
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Which linter would you like to use? ESLint / Biome / None
Would you like to use Tailwind CSS? No / Yes
Would you like your code inside a `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the import alias (@/* by default)? No / Yes
Each of these choices shapes the project in a meaningful way. It is worth understanding what each one does before making a decision.
TypeScript
TypeScript is the recommended choice for any serious Next.js project. Next.js is itself written in TypeScript, its documentation examples default to TypeScript, and the framework's type definitions provide accurate auto-completion and compile-time safety across routes, components, and API handlers. Choosing TypeScript here configures the project with a tsconfig.json and sets up the compiler correctly — no additional setup is needed.
Linter
A linter analyses code for potential errors and enforces style consistency. ESLint is the established choice with broad plugin support and deep Next.js integration. Biome is a newer, significantly faster alternative that combines linting and formatting in a single tool. Either is a reasonable choice. ESLint has more community resources and is more commonly found in existing Next.js projects.
App Router
This should always be Yes for any new project. The App Router is the current architecture of Next.js, the direction of all active development, and the system this research series covers in full. The alternative — the Pages Router — is a legacy system maintained for existing applications.
src/ Directory
This is a structural preference. Choosing Yes places all application code inside a src/ folder, keeping it separate from configuration files at the root. The application behaves identically either way. For larger projects, the src/ structure keeps the root directory cleaner. For smaller ones, it adds a layer of nesting that serves no functional purpose. Both are widely used in real Next.js projects.
Import Alias
The default import alias @/* maps to the root of the project, allowing imports like:
import { Button } from '@/components/Button'
Instead of relative paths that grow unwieldy as the project deepens:
import { Button } from '../../../components/Button'
The default @/* is the convention the vast majority of Next.js projects use. There is rarely a reason to change it.
What Gets Created
After the prompt completes and dependencies are installed, the project directory contains the following structure:
my-app/
├── app/
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.ts
├── package.json
├── tsconfig.json
└── eslint.config.mjs
This is a minimal but complete Next.js application. Every file here has a specific purpose.
app/ is the heart of the project. This directory is where routes, layouts, components, and API handlers live. The App Router reads the structure of this directory to determine the application's routes. Every file and folder convention understood by Next.js applies within app/.
app/layout.tsx is the root layout — the outermost wrapper applied to every page in the application. It must contain the <html> and <body> tags. Every route inherits from this layout.
app/page.tsx is the home page. It is the component rendered when a user visits the root path /. This is the first file that will change when building out an application.
app/globals.css contains global CSS styles applied to the entire application. It is imported directly inside layout.tsx.
public/ stores static assets — images, fonts, icons, and other files — that are served directly at the root URL. A file placed at public/logo.png becomes accessible at /logo.png.
next.config.ts is the configuration file for Next.js itself. Most applications need very little in here to begin with, but it is where features like custom headers, redirects, environment configuration, and advanced build settings are managed.
tsconfig.json is the TypeScript configuration. Next.js preconfigures this correctly, and it rarely needs to be changed directly — though the import alias path mappings live here.
eslint.config.mjs contains linting rules. Next.js provides a sensible default configuration that enforces best practices specific to the framework.
Running the Development Server
With the project created, starting the development server is a single command run from inside the project directory:
cd my-app
npm run dev
This starts Next.js in development mode at http://localhost:3000. The development server supports Fast Refresh — changes saved to any file appear in the browser almost instantly, without a full page reload and without losing component state. This makes the development experience fast and tight.
The terminal output shows something like:
▲ Next.js 15.x.x
- Local: http://localhost:3000
- Ready in 847ms
Visiting http://localhost:3000 in a browser shows the default Next.js welcome page — the rendered output of app/page.tsx. Replacing that file's contents with something new immediately updates what appears in the browser.
The Development Build vs the Production Build
The development server is not the same as the production application. In development, Next.js skips certain optimisations in favour of fast refresh and detailed error messages. Code is not minified, images are not fully optimised, and some rendering behaviour differs from what users experience in production.
Building for production is a separate step:
npm run build
This produces an optimised build in the .next/ directory — minified JavaScript, pre-rendered pages where applicable, and a manifest of all routes and their rendering strategies. The output of this command is what gets deployed.
To run the production build locally before deploying it:
npm run start
This starts a Node.js server serving the production build at http://localhost:3000. It is a useful step for catching issues that only appear in production before they reach users.
The full set of npm scripts created by create-next-app is:
| Script | Command | Purpose |
|---|---|---|
npm run dev | next dev | Start the development server with Fast Refresh |
npm run build | next build | Build the application for production |
npm run start | next start | Serve the production build locally |
npm run lint | eslint | Run the linter across the project |
Adding Next.js to an Existing Project
Not every project starts from scratch. Adding Next.js to an existing codebase — one that already has a package.json — is done by installing the required packages directly:
npm install next@latest react@latest react-dom@latest
Then adding the scripts to package.json:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
}
}
And creating the minimum required files — an app/ directory with at least a layout.tsx and a page.tsx — following the same conventions as a freshly scaffolded project. This approach is less common than using create-next-app but is straightforward for teams migrating an existing React project incrementally.
Environment Variables
Next.js has built-in support for environment variables through .env files, following the same conventions established in the Node.js series. A .env.local file in the project root is loaded automatically in both development and production and is the standard place for secrets and configuration values that should not be committed to version control.
# .env.local
DATABASE_URL=postgresql://localhost:5432/myapp
API_SECRET=your-secret-key
Next.js adds one important distinction on top of the standard environment variable model: the boundary between server and client.
By default, environment variables in Next.js are server-only. They are available in server-side code — Server Components, Route Handlers, middleware — but are not sent to the browser. This is the correct behaviour for secrets.
Variables that need to be accessible in the browser must be prefixed with NEXT_PUBLIC_:
NEXT_PUBLIC_API_URL=https://api.example.com
Only variables with this prefix are included in the JavaScript bundle sent to the client. Any variable without it stays on the server, invisible to browser-side code. This is a simple but important distinction — a secret API key should never carry the NEXT_PUBLIC_ prefix, and a public configuration value that frontend code needs to read requires it.
Upgrading Next.js
Next.js releases updates regularly. Staying on a recent version is worthwhile — updates carry performance improvements, security patches, and new features. The framework ships an upgrade command that handles the process:
npx next upgrade
This updates Next.js to the latest version and applies any available codemods — automated code transformations that handle breaking changes in the API without requiring manual editing. Major version upgrades may still require reviewing the migration guide at nextjs.org, but the upgrade command handles the mechanical parts.
Summary
| Task | Command |
|---|---|
| Create a new project | npx create-next-app@latest my-app |
| Start the development server | npm run dev |
| Build for production | npm run build |
| Run the production build locally | npm run start |
| Run the linter | npm run lint |
| Upgrade Next.js | npx next upgrade |