Next.js Cheat Sheet
Complete reference guide for Next.js with interactive examples and live playground links
Getting Started
Create a New Project
Create a new Next.js project with TypeScript
# Create a new Next.js project with TypeScript
npx create-next-app@latest my-app --typescript
# Using pnpm
pnpm create next-app my-app --typescript
# Using Yarn
yarn create next-app my-app --typescript
Project Structure
Basic structure of a Next.js project with TypeScript
my-app/
├── app/ # App Router (Next.js 13+)
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ └── [...]/ # Other routes
├── pages/ # Pages Router (traditional)
│ ├── _app.tsx # Custom App component
│ ├── _document.tsx # Custom Document
│ ├── index.tsx # Home page
│ └── api/ # API routes
├── public/ # Static files
├── components/ # React components
├── styles/ # CSS files
├── next.config.js # Next.js configuration
├── tsconfig.json # TypeScript configuration
└── package.json # Dependencies
TypeScript Configuration
Default TypeScript configuration for Next.js
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
Routing
App Router (Next.js 13+)
File-based routing with the App Router (Next.js 13+)
// app/page.tsx - Home page
export default function Home() {
return <h1>Hello, Next.js!</h1>
}
// app/about/page.tsx - About page
export default function About() {
return <h1>About Us</h1>
}
// app/blog/[slug]/page.tsx - Dynamic route
export default function BlogPost({ params }: { params: { slug: string } }) {
return <h1>Blog Post: {params.slug}</h1>
}
// app/blog/[...slug]/page.tsx - Catch-all route
export default function BlogPosts({ params }: { params: { slug: string[] } }) {
return <h1>Blog Posts: {params.slug.join('/')}</h1>
}
Pages Router (Traditional)
File-based routing with the Pages Router using TypeScript
// pages/index.tsx - Home page
import type { NextPage } from 'next'
const Home: NextPage = () => {
return <h1>Hello, Next.js!</h1>
}
export default Home
// pages/blog/[slug].tsx - Dynamic route
import type { GetServerSideProps, NextPage } from 'next'
interface Props {
slug: string
}
const BlogPost: NextPage<Props> = ({ slug }) => {
return <h1>Blog Post: {slug}</h1>
}
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
const slug = params?.slug as string
return { props: { slug } }
}
export default BlogPost
Data Fetching
Server Components (App Router)
Server Components for data fetching with TypeScript
// app/users/page.tsx
// Server Component (default in App Router)
interface User {
id: number
name: string
email: string
}
async function UsersPage() {
// This runs on the server
const res = await fetch('https://api.example.com/users')
const users: User[] = await res.json()
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
)
}
export default UsersPage
Client Components (App Router)
Client Components with TypeScript
// app/components/counter.tsx
'use client' // Mark as Client Component
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState<number>(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
)
}
API Routes
API Routes (Pages Router)
Creating API endpoints with TypeScript
// pages/api/users.ts
import type { NextApiRequest, NextApiResponse } from 'next'
type User = {
id: number
name: string
}
type ResponseData = {
users?: User[]
message?: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
const { method } = req
switch (method) {
case 'GET':
// Get users
res.status(200).json({
users: [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]
})
break
case 'POST':
// Create user
const { name } = req.body
res.status(201).json({ message: `User ${name} created` })
break
default:
res.setHeader('Allow', ['GET', 'POST'])
res.status(405).end(`Method ${method} Not Allowed`)
}
}
Route Handlers (App Router)
Creating API endpoints with App Router and TypeScript
// app/api/users/route.ts
import { NextResponse } from 'next/server'
interface User {
id: number
name: string
}
// GET handler
export async function GET() {
const users: User[] = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]
return NextResponse.json(users)
}
// POST handler
export async function POST(request: Request) {
const data = await request.json()
return NextResponse.json({ message: 'User created', data })
}
// Dynamic route handler
// app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const id = params.id
return NextResponse.json({ id, name: 'John Doe' })
}
TypeScript Patterns
Common Types
Common TypeScript types and patterns for Next.js
// Common TypeScript types for Next.js
// Pages Router
import type {
NextPage,
GetStaticProps,
GetStaticPaths,
GetServerSideProps,
NextApiRequest,
NextApiResponse
} from 'next'
// Page component with props
interface HomeProps {
posts: Post[]
totalCount: number
}
const Home: NextPage<HomeProps> = ({ posts, totalCount }) => {
// ...
}
// GetStaticProps with typed return
export const getStaticProps: GetStaticProps<HomeProps> = async () => {
// ...
return {
props: {
posts: [],
totalCount: 0
},
revalidate: 60
}
}
// App Router
// Route params
interface PageParams {
slug: string
}
// Page props
interface PageProps {
params: PageParams
searchParams: { [key: string]: string | string[] | undefined }
}
export default function Page({ params, searchParams }: PageProps) {
// ...
}
Type-Safe API Calls
Type-safe API calls in Next.js
// Define API response types
interface User {
id: number
name: string
email: string
}
// Type-safe API client function
async function fetchUsers(): Promise<User[]> {
const res = await fetch('/api/users')
if (!res.ok) {
throw new Error('Failed to fetch users')
}
return res.json()
}
// Using with SWR
import useSWR from 'swr'
function UserList() {
const { data, error, isLoading } = useSWR<User[], Error>(
'/api/users',
fetchUsers
)
if (error) return <div>Failed to load</div>
if (isLoading) return <div>Loading...</div>
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
Optimization
Image Component
Using the Next.js Image component with TypeScript
// Using the Image component with TypeScript
import Image from 'next/image'
interface ProfileProps {
src: string
alt: string
priority?: boolean
}
export function Profile({ src, alt, priority = false }: ProfileProps) {
return (
<div>
<Image
src={src}
alt={alt}
width={500}
height={300}
priority={priority}
/>
</div>
)
}
// Usage
<Profile
src="/images/profile.jpg"
alt="User profile"
priority
/>
Environment Variables
Type-safe environment variables
// env.d.ts - Type definitions for environment variables
declare namespace NodeJS {
interface ProcessEnv {
NEXT_PUBLIC_API_URL: string
DATABASE_URL: string
API_KEY: string
}
}
// Using environment variables
const apiUrl = process.env.NEXT_PUBLIC_API_URL
const apiKey = process.env.API_KEY // Only available server-side
Next.js - Interactive Developer Reference
Hover over code blocks to copy or run in live playground