Chapter 13: Common Errors and Solutions
What are Common Next.js Errors?
Common Next.js errors are issues that developers frequently encounter when building applications. These errors can range from configuration problems to runtime issues, and understanding how to identify and resolve them is crucial for efficient development.
Error Categories:
- Build Errors: Issues during the build process
- Runtime Errors: Problems that occur when the application is running
- Configuration Errors: Misconfigurations in Next.js settings
- Performance Issues: Problems affecting application performance
- Deployment Errors: Issues when deploying to production
Why Understanding Common Errors is Important?
Benefits:
- Faster Development: Quick identification and resolution of issues
- Better Debugging: Understanding error patterns and solutions
- Prevention: Avoiding common mistakes before they occur
- Productivity: Less time spent troubleshooting
- Learning: Understanding how Next.js works internally
- Confidence: Building robust applications with error handling
How to Debug and Solve Common Errors
1. Build Errors
Error: "Module not found"
# Error message
Module not found: Can't resolve 'module-name' in '/path/to/file'
Solutions:
# Check if the module is installed
npm list module-name
# Install missing dependency
npm install module-name
# For TypeScript modules
npm install @types/module-name
# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install
Error: "Cannot resolve module" in TypeScript
// Error: Cannot find module '@/components/Button' or its corresponding type declarations
// Solution 1: Check tsconfig.json paths
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"]
}
}
}
// Solution 2: Use relative imports
import Button from '../components/Button'
// Solution 3: Check file extensions
import Button from './Button.tsx'
Error: "Build failed due to TypeScript errors"
// Error: Property 'title' does not exist on type '{}'
// Solution: Add proper type definitions
interface Post {
title: string
content: string
published: boolean
}
export default function BlogPost({ post }: { post: Post }) {
return <h1>{post.title}</h1>
}
// Or use type assertion (not recommended for production)
export default function BlogPost({ post }: { post: any }) {
return <h1>{post.title}</h1>
}
2. Runtime Errors
Error: "Hydration mismatch"
// Error: Text content does not match server-rendered HTML
// Problem: Different content on server and client
export default function Component() {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
// This will cause hydration mismatch
return <div>{mounted ? 'Client' : 'Server'}</div>
}
// Solution: Use dynamic imports with ssr: false
import dynamic from 'next/dynamic'
const ClientOnlyComponent = dynamic(() => import('./ClientOnly'), {
ssr: false
})
export default function Page() {
return <ClientOnlyComponent />
}
// Or use useEffect to handle client-side only content
export default function Component() {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) {
return <div>Loading...</div>
}
return <div>Client content</div>
}
Error: "Cannot read property of undefined"
// Error: Cannot read property 'title' of undefined
// Problem: Accessing properties before data is loaded
export default function BlogPost({ post }) {
return <h1>{post.title}</h1> // post might be undefined
}
// Solution: Add proper error handling and loading states
export default function BlogPost({ post, loading, error }) {
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
if (!post) return <div>Post not found</div>
return <h1>{post.title}</h1>
}
// Or use optional chaining
export default function BlogPost({ post }) {
return <h1>{post?.title || 'Untitled'}</h1>
}
Error: "useEffect has missing dependencies"
// Warning: React Hook useEffect has missing dependencies
// Problem: Missing dependencies in useEffect
function Component({ userId }) {
const [user, setUser] = useState(null)
useEffect(() => {
fetchUser(userId).then(setUser)
}, []) // Missing userId dependency
return <div>{user?.name}</div>
}
// Solution: Add missing dependencies
useEffect(() => {
fetchUser(userId).then(setUser)
}, [userId])
// Or use useCallback to memoize functions
const fetchUserData = useCallback(async () => {
const userData = await fetchUser(userId)
setUser(userData)
}, [userId])
useEffect(() => {
fetchUserData()
}, [fetchUserData])
3. Configuration Errors
Error: "Invalid next.config.js"
// Error: Invalid configuration object
// Problem: Incorrect configuration syntax
module.exports = {
experimental: {
appDir: true // This might cause issues in newer versions
}
}
// Solution: Check Next.js version and update configuration
// For Next.js 13.4+
module.exports = {
experimental: {
// Remove deprecated options
}
}
// For proper configuration
module.exports = {
images: {
domains: ['example.com'],
},
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.example.com/:path*',
},
]
},
}
Error: "Environment variables not found"
# Error: Environment variable not found
# Problem: Using server-side env vars on client
const apiKey = process.env.API_KEY // This won't work on client
# Solution: Use NEXT_PUBLIC_ prefix for client-side variables
const apiKey = process.env.NEXT_PUBLIC_API_KEY
# Or use server-side only in API routes
// pages/api/data.js
export default function handler(req, res) {
const apiKey = process.env.API_KEY // This works in API routes
// ...
}
Error: "Image optimization failed"
// Error: Image optimization failed
// Problem: Invalid image source or configuration
<Image src="/invalid-image.jpg" width={500} height={300} />
// Solution: Check image source and add proper configuration
// next.config.js
module.exports = {
images: {
domains: ['example.com'],
formats: ['image/webp', 'image/avif'],
},
}
// Use proper image sources
<Image
src="https://example.com/image.jpg"
width={500}
height={300}
alt="Description"
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
/>
4. API Route Errors
Error: "API route not found"
// Error: 404 - API route not found
// Problem: Incorrect file structure or naming
// Wrong: pages/api/users.js (should be users.js, not user.js)
// Solution: Check file structure
pages/
api/
users.js // ✅ Correct
users/
[id].js // ✅ Correct for /api/users/[id]
auth/
login.js // ✅ Correct for /api/auth/login
Error: "CORS policy error"
// Error: CORS policy: No 'Access-Control-Allow-Origin' header
// Problem: Missing CORS headers in API routes
export default function handler(req, res) {
res.status(200).json({ data: 'test' })
}
// Solution: Add CORS headers
export default function handler(req, res) {
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
// Handle preflight requests
if (req.method === 'OPTIONS') {
res.status(200).end()
return
}
res.status(200).json({ data: 'test' })
}
Error: "Request body too large"
// Error: Request entity too large
// Problem: Default body size limit exceeded
export default function handler(req, res) {
// Large file upload fails
}
// Solution: Configure body size limit
// next.config.js
module.exports = {
api: {
bodyParser: {
sizeLimit: '10mb',
},
},
}
// Or disable body parsing for specific routes
export const config = {
api: {
bodyParser: false,
},
}
export default function handler(req, res) {
// Handle raw body
}
5. Performance Issues
Error: "Bundle size too large"
# Warning: Bundle size exceeds recommended limit
# Problem: Large dependencies or inefficient imports
import * as _ from 'lodash' // Imports entire library
# Solution: Use specific imports
import { debounce, throttle } from 'lodash'
// Or use dynamic imports
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <div>Loading...</div>
})
// Analyze bundle size
npm install --save-dev @next/bundle-analyzer
Error: "Memory leak detected"
// Problem: Event listeners not cleaned up
useEffect(() => {
const handleResize = () => {
// Handle resize
}
window.addEventListener('resize', handleResize)
// Missing cleanup
}, [])
// Solution: Clean up event listeners
useEffect(() => {
const handleResize = () => {
// Handle resize
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
Error: "Too many re-renders"
// Error: Too many re-renders. React limits the number of renders
// Problem: State update in render
function Component() {
const [count, setCount] = useState(0)
// This causes infinite re-renders
setCount(count + 1)
return <div>{count}</div>
}
// Solution: Use useEffect for side effects
function Component() {
const [count, setCount] = useState(0)
useEffect(() => {
setCount(prev => prev + 1)
}, [])
return <div>{count}</div>
}
6. Deployment Errors
Error: "Build failed on Vercel"
# Error: Build failed
# Common causes and solutions:
# 1. Environment variables missing
# Solution: Add environment variables in Vercel dashboard
# 2. Node.js version mismatch
# Solution: Add .nvmrc file
echo "18" > .nvmrc
# 3. Build command issues
# Solution: Check package.json scripts
{
"scripts": {
"build": "next build",
"start": "next start"
}
}
# 4. TypeScript errors
# Solution: Fix TypeScript errors or add type checking to build
{
"scripts": {
"build": "next build",
"type-check": "tsc --noEmit"
}
}
Error: "Static export failed"
// Error: Static export failed
// Problem: Using server-side features in static export
export async function getServerSideProps() {
// This won't work with static export
}
// Solution: Use getStaticProps for static export
export async function getStaticProps() {
return {
props: {
data: 'static data'
}
}
}
// Or configure for static export
// next.config.js
module.exports = {
output: 'export',
trailingSlash: true,
images: {
unoptimized: true
}
}
Debugging Tools and Techniques
1. Next.js Built-in Debugging
Enable Debug Mode:
# Enable Next.js debug mode
DEBUG=* npm run dev
# Or specific debug namespaces
DEBUG=next:* npm run dev
Use Next.js Error Overlay:
// The error overlay will show detailed error information
// Click on the error to see the stack trace
// Use the "X" button to dismiss the overlay
2. Browser DevTools
React Developer Tools:
// Install React DevTools browser extension
// Use Components tab to inspect component tree
// Use Profiler tab to identify performance issues
Network Tab:
// Check for failed requests
// Monitor bundle sizes
// Analyze loading times
3. Custom Error Handling
Error Boundary:
// components/ErrorBoundary.js
import { Component } from 'react'
class ErrorBoundary extends Component {
constructor(props) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error) {
return { hasError: true, error }
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo)
// Send to error tracking service
if (process.env.NODE_ENV === 'production') {
// Send error to monitoring service
}
}
render() {
if (this.state.hasError) {
return (
<div className="error-fallback">
<h2>Something went wrong.</h2>
<button onClick={() => this.setState({ hasError: false, error: null })}>
Try again
</button>
</div>
)
}
return this.props.children
}
}
export default ErrorBoundary
API Error Handling:
// lib/api.js
export async function apiRequest(url, options = {}) {
try {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options.headers,
},
...options,
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return await response.json()
} catch (error) {
console.error('API request failed:', error)
throw error
}
}
Prevention Strategies
1. Code Quality Tools
ESLint Configuration:
// .eslintrc.js
module.exports = {
extends: [
'next/core-web-vitals',
'eslint:recommended',
'@typescript-eslint/recommended',
],
rules: {
'no-unused-vars': 'error',
'no-console': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
},
}
TypeScript Strict Mode:
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true
}
}
2. Testing
Unit Testing:
// __tests__/components/Button.test.js
import { render, screen } from '@testing-library/react'
import Button from '../components/Button'
test('renders button with text', () => {
render(<Button>Click me</Button>)
expect(screen.getByText('Click me')).toBeInTheDocument()
})
Integration Testing:
// __tests__/api/posts.test.js
import { createMocks } from 'node-mocks-http'
import handler from '../../pages/api/posts'
test('GET /api/posts returns posts', async () => {
const { req, res } = createMocks({
method: 'GET',
})
await handler(req, res)
expect(res._getStatusCode()).toBe(200)
expect(JSON.parse(res._getData())).toHaveProperty('posts')
})
3. Monitoring and Logging
Error Tracking:
// lib/errorTracking.js
export function trackError(error, context = {}) {
if (process.env.NODE_ENV === 'production') {
// Send to error tracking service
console.error('Error tracked:', error, context)
}
}
// Usage
try {
// Risky operation
} catch (error) {
trackError(error, { component: 'Button', action: 'click' })
}
Performance Monitoring:
// lib/performance.js
export function trackPerformance(name, startTime) {
const endTime = performance.now()
const duration = endTime - startTime
if (duration > 100) { // Log slow operations
console.warn(`Slow operation: ${name} took ${duration}ms`)
}
}
// Usage
const startTime = performance.now()
// Expensive operation
trackPerformance('dataProcessing', startTime)
Best Practices for Error Prevention
1. Defensive Programming
// ✅ Good: Defensive programming
export default function UserProfile({ user }) {
if (!user) {
return <div>User not found</div>
}
return (
<div>
<h1>{user.name || 'Unknown User'}</h1>
<p>{user.email || 'No email provided'}</p>
</div>
)
}
// ❌ Bad: Assuming data exists
export default function UserProfile({ user }) {
return (
<div>
<h1>{user.name}</h1> {/* Will crash if user is null */}
<p>{user.email}</p>
</div>
)
}
2. Proper Error Boundaries
// ✅ Good: Error boundaries at appropriate levels
function App() {
return (
<ErrorBoundary>
<Header />
<ErrorBoundary>
<MainContent />
</ErrorBoundary>
<Footer />
</ErrorBoundary>
)
}
3. Input Validation
// ✅ Good: Validate inputs
import { z } from 'zod'
const userSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(0).max(120),
})
export default function CreateUser({ userData }) {
try {
const validatedData = userSchema.parse(userData)
// Process validated data
} catch (error) {
console.error('Validation error:', error)
return <div>Invalid user data</div>
}
}
Common Mistakes to Avoid
1. Incorrect Import/Export Usage
// ❌ Wrong: Mixing default and named exports
import Button, { ButtonProps } from './Button'
// Button.js exports: export default Button, export type ButtonProps
// ✅ Correct: Consistent export pattern
import Button from './Button'
import type { ButtonProps } from './Button'
2. Memory Leaks
// ❌ Wrong: Not cleaning up subscriptions
useEffect(() => {
const subscription = eventEmitter.subscribe(handleEvent)
// Missing cleanup
}, [])
// ✅ Correct: Proper cleanup
useEffect(() => {
const subscription = eventEmitter.subscribe(handleEvent)
return () => {
subscription.unsubscribe()
}
}, [])
3. Inefficient Re-renders
// ❌ Wrong: Creating objects in render
function Component({ items }) {
const processedItems = items.map(item => ({
...item,
processed: true
}))
return <ItemList items={processedItems} />
}
// ✅ Correct: Memoize expensive operations
function Component({ items }) {
const processedItems = useMemo(() =>
items.map(item => ({
...item,
processed: true
})), [items]
)
return <ItemList items={processedItems} />
}
Summary
Understanding and resolving common Next.js errors is essential for efficient development:
- Build errors often relate to missing dependencies or TypeScript issues
- Runtime errors commonly involve hydration mismatches and undefined values
- Configuration errors usually stem from incorrect Next.js settings
- Performance issues can be caused by inefficient code or memory leaks
- Deployment errors often relate to environment variables or build configuration
Key Takeaways:
- Use proper error boundaries and error handling
- Implement defensive programming practices
- Monitor and log errors in production
- Test your code thoroughly
- Follow Next.js best practices and conventions
- Use debugging tools effectively
This tutorial is part of the comprehensive Next.js learning path at syscook.dev. You've now completed the full Next.js tutorial from beginner to expert!
Author: syscook.dev
Last Updated: December 2024