Next.js New Project Setup: Optimal Flow and Best Practices
Next.js New Project Setup: Optimal Flow and Best Practices
Starting a new Next.js project can be overwhelming with all the configuration options available. This guide will walk you through the optimal setup process, from initialization to production-ready configuration.
Step 1: Project Initialization
Create New Next.js Project
Use the official Next.js CLI to create a new project:
# Create new Next.js app with TypeScript
npx create-next-app@latest my-app --typescript --tailwind --eslint --app
# Or with JavaScript
npx create-next-app@latest my-app --js --tailwind --eslint --appProject Structure
After initialization, your project should have this structure:
my-app/
├── app/ # App Router (Next.js 13+)
│ ├── layout.js # Root layout
│ ├── page.js # Home page
│ └── globals.css # Global styles
├── public/ # Static assets
├── .next/ # Build output (gitignored)
├── node_modules/ # Dependencies (gitignored)
├── .eslintrc.json # ESLint config
├── .gitignore # Git ignore rules
├── next.config.js # Next.js config
├── package.json # Dependencies
├── postcss.config.js # PostCSS config
├── tailwind.config.js # Tailwind config
└── tsconfig.json # TypeScript config (if using TS)Step 2: Essential Configuration
Update next.config.js
Configure Next.js for optimal performance:
/** @type {import('next').NextConfig} */
const nextConfig = {
// Enable React strict mode
reactStrictMode: true,
// Optimize images
images: {
domains: ['example.com'],
formats: ['image/avif', 'image/webp'],
},
// Compress output
compress: true,
// Enable standalone output for DockerStep 3: Optimal Folder Structure
Organize your project with this structure:
my-app/
├── app/
│ ├── (auth)/ # Route group for auth pages
│ │ ├── login/
│ │ └── register/
│ ├── (dashboard)/ # Route group for dashboard
│ │ ├── layout.js
│ │ └── page.js
│ ├── api/ # API routes
│ │ └── users/
│ │ └── route.js
│ ├── layout.js # Root layout
│ ├── page.js # Home page
│ └── globals.css
├── components/Step 4: Install Essential Dependencies
Core Dependencies
# Form handling
npm install react-hook-form zod @hookform/resolvers
# State management
npm install zustand # or redux toolkit
# HTTP client
npm install axios
# Date handling
npm install date-fns
# Icons
npm install react-icons
Development Dependencies
# Code formatting
npm install -D prettier prettier-plugin-tailwindcss
# Additional linting
npm install -D @typescript-eslint/eslint-plugin
# Testing
npm install -D jest @testing-library/react @testing-library/jest-dom
# Type checking (if using TypeScript)
npm install -D typescript @types/node @types/reactStep 5: Environment Variables Setup
Create environment variable files:
.env.local (for local development):
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
# API
NEXT_PUBLIC_API_URL=http://localhost:3000/api
# Authentication
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key
# Third-party services
STRIPE_PUBLIC_KEY=pk_test_...
GOOGLE_CLIENT_ID=....env.example (template for team):
DATABASE_URL=
NEXT_PUBLIC_API_URL=
NEXTAUTH_URL=
NEXTAUTH_SECRET=Step 6: Configure ESLint and Prettier
.eslintrc.json
{
"extends": [
"next/core-web-vitals",
"prettier"
],
"rules": {
"react/no-unescaped-entities": "off",
"@next/next/no-img-element": "warn"
}
}.prettierrc
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"plugins": [
"prettier-plugin-tailwindcss"
]
}.prettierignore
.next
node_modules
public
*.min.jsStep 7: Set Up Root Layout
Update app/layout.js:
import { Inter } from 'next/font/google'
import './globals.css'
import Header from '@/components/layout/Header'
import Footer from '@/components/layout/Footer'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: 'My App',
description: 'Description of my app',
keywords: ['nextjs', 'react', 'web development'],
}
export default function RootLayout({ children }) {
return (Step 8: Create Utility Functions
Create lib/utils.js:
// Format date
export function formatDate(date) {
return new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
}
// Debounce function
export function debounce(func, wait) {
let timeout
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout)Step 9: Set Up API Routes
Create API route example app/api/users/route.js:
import { NextResponse } from 'next/server'
export async function GET(request) {
try {
const users = await fetchUsers() // Your data fetching logic
return NextResponse.json({ users })
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch users' },
{ status: 500 }
)
}
}
export async function POST(request) {Step 10: Development Workflow
Package.json Scripts
Update package.json scripts:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"format": "prettier --write .",
"format:check": "prettier --check .",
"type-check": "tsc --noEmit",
"test": "jest",
"test:watch": "jest --watch"
}
}Git Workflow
1. Create feature branch: git checkout -b feature/new-feature
2. Make changes: Write code, add tests
3. Format code: npm run format
4. Lint code: npm run lint
5. Commit: git commit -m "feat: add new feature"
6. Push: git push origin feature/new-feature
7. Create PR: Open pull request on GitHub
Step 11: Performance Optimization
Image Optimization
import Image from 'next/image'
export default function MyComponent() {
return (
<Image
src="/image.jpg"
alt="Description"
width={500}
height={300}
priority // For above-the-fold images
placeholder="blur" // Optional
/>
)
}Font Optimization
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
})Code Splitting
Use dynamic imports for large components:
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('@/components/HeavyComponent'), {
loading: () => <p>Loading...</p>,
ssr: false, // Disable SSR if needed
})Step 12: Testing Setup
Jest Configuration
Create jest.config.js:
const nextJest = require('next/jest')
const createJestConfig = nextJest({
dir: './',
})
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testEnvironment: 'jest-environment-jsdom',
}
module.exports = createJestConfig(customJestConfig)Example Test
import { render, screen } from '@testing-library/react'
import Home from '@/app/page'
describe('Home', () => {
it('renders heading', () => {
render(<Home />)
const heading = screen.getByRole('heading', {
name: /welcome/i,
})
expect(heading).toBeInTheDocument()
})
})Best Practices Summary
1. **Use TypeScript**
TypeScript catches errors early and improves developer experience.
2. **Follow File Naming Conventions**
3. **Optimize Images**
Always use Next.js Image component for better performance.
4. **Use Server Components**
Prefer Server Components when possible for better performance.
5. **Implement Error Boundaries**
Handle errors gracefully with error boundaries.
6. **Add Loading States**
Use loading.tsx files for better UX.
7. **Environment Variables**
Never commit sensitive data. Use .env.local for secrets.
8. **Code Organization**
Keep components small and focused. Use feature-based folder structure.
Conclusion
Following this setup guide will give you a solid foundation for your Next.js project. Remember to:
Your Next.js project is now ready for development! 🚀
Share this article
Enjoyed this article?
Support our work and help us create more free content for developers.
Stay Updated
Get the latest articles and updates delivered to your inbox.