Next.js Samples

Next.js framework examples - The React Framework for Production, with server-side rendering, routing, and modern web features

💻 Next.js Basics and Components javascript

🟢 simple ⭐⭐

Fundamental Next.js concepts including components, pages, and basic routing

⏱️ 25 min 🏷️ nextjs, react, frontend, components, routing
Prerequisites: React basics, JavaScript ES6+, HTML/CSS
// Next.js Basics and Components

// 1. Basic Next.js App Structure
/*
my-next-app/
├── pages/
│   ├── index.js          # Home page
│   ├── about.js          # About page
│   └── api/
│       └── users.js       # API route
├── components/
│   ├── Header.js         # Reusable header
│   └── Footer.js         # Reusable footer
├── styles/
│   └── globals.css       # Global styles
├── public/               # Static assets
└── package.json
*/

// 2. Basic Page Component (pages/index.js)
import Head from 'next/head'
import Link from 'next/link'
import Header from '../components/Header'
import Footer from '../components/Footer'

export default function HomePage() {
  return (
    <>
      <Head>
        <title>My Next.js App</title>
        <meta name="description" content="Welcome to my Next.js application" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <div className="container">
        <Header />

        <main>
          <h1>Welcome to Next.js!</h1>
          <p>This is a basic Next.js application.</p>

          <section className="features">
            <h2>Features</h2>
            <ul>
              <li>Server-Side Rendering (SSR)</li>
              <li>Static Site Generation (SSG)</li>
              <li>API Routes</li>
              <li>Image Optimization</li>
              <li>Automatic Code Splitting</li>
            </ul>
          </section>

          <section className="navigation">
            <h2>Navigation</h2>
            <nav>
              <Link href="/about">
                <a>About Us</a>
              </Link>
              <Link href="/blog">
                <a>Blog</a>
              </Link>
              <Link href="/api/users">
                <a>Users API</a>
              </Link>
            </nav>
          </section>
        </main>

        <Footer />
      </div>

      <style jsx>{`
        .container {
          max-width: 1200px;
          margin: 0 auto;
          padding: 0 20px;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }

        main {
          padding: 40px 0;
        }

        h1 {
          color: #333;
          margin-bottom: 20px;
        }

        .features {
          margin: 40px 0;
        }

        .features ul {
          list-style-type: none;
          padding: 0;
        }

        .features li {
          padding: 10px 0;
          border-bottom: 1px solid #eee;
        }

        .features li:before {
          content: "✓";
          color: #4CAF50;
          font-weight: bold;
          margin-right: 10px;
        }

        .navigation {
          margin: 40px 0;
        }

        .navigation nav {
          display: flex;
          gap: 20px;
        }

        .navigation a {
          padding: 10px 20px;
          background-color: #0070f3;
          color: white;
          text-decoration: none;
          border-radius: 5px;
          transition: background-color 0.3s;
        }

        .navigation a:hover {
          background-color: #0051cc;
        }
      `}</style>
    </>
  )
}

// 3. Reusable Header Component (components/Header.js)
import Link from 'next/link'
import { useState } from 'react'

export default function Header() {
  const [isMenuOpen, setIsMenuOpen] = useState(false)

  return (
    <header className="header">
      <div className="header-content">
        <Link href="/">
          <a className="logo">MyApp</a>
        </Link>

        <nav className={`nav ${isMenuOpen ? 'open' : ''}`}>
          <Link href="/">
            <a>Home</a>
          </Link>
          <Link href="/about">
            <a>About</a>
          </Link>
          <Link href="/blog">
            <a>Blog</a>
          </Link>
          <Link href="/contact">
            <a>Contact</a>
          </Link>
        </nav>

        <button
          className="menu-toggle"
          onClick={() => setIsMenuOpen(!isMenuOpen)}
        >
          {isMenuOpen ? '✕' : '☰'}
        </button>
      </div>

      <style jsx>{`
        .header {
          background-color: #fff;
          box-shadow: 0 2px 4px rgba(0,0,0,0.1);
          position: sticky;
          top: 0;
          z-index: 1000;
        }

        .header-content {
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 1rem 2rem;
          max-width: 1200px;
          margin: 0 auto;
        }

        .logo {
          font-size: 1.5rem;
          font-weight: bold;
          color: #0070f3;
          text-decoration: none;
        }

        .nav {
          display: flex;
          gap: 2rem;
        }

        .nav a {
          color: #333;
          text-decoration: none;
          font-weight: 500;
          transition: color 0.3s;
        }

        .nav a:hover {
          color: #0070f3;
        }

        .menu-toggle {
          display: none;
          background: none;
          border: none;
          font-size: 1.5rem;
          cursor: pointer;
        }

        @media (max-width: 768px) {
          .nav {
            position: absolute;
            top: 100%;
            left: 0;
            right: 0;
            background: white;
            flex-direction: column;
            padding: 1rem;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            display: none;
          }

          .nav.open {
            display: flex;
          }

          .menu-toggle {
            display: block;
          }
        }
      `}</style>
    </>
  )
}

// 4. About Page with Dynamic Data (pages/about.js)
import Head from 'next/head'
import { useState, useEffect } from 'react'

export default function AboutPage() {
  const [team, setTeam] = useState([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    // Simulate API call
    fetchTeamData()
  }, [])

  async function fetchTeamData() {
    try {
      // In real app, this would be an actual API call
      setTimeout(() => {
        setTeam([
          { id: 1, name: 'John Doe', role: 'CEO', bio: 'Visionary leader with 10+ years experience' },
          { id: 2, name: 'Jane Smith', role: 'CTO', bio: 'Tech expert specializing in modern web technologies' },
          { id: 3, name: 'Mike Johnson', role: 'Designer', bio: 'Creative mind behind our beautiful designs' }
        ])
        setLoading(false)
      }, 1000)
    } catch (error) {
      console.error('Error fetching team data:', error)
      setLoading(false)
    }
  }

  return (
    <>
      <Head>
        <title>About Us - My Next.js App</title>
        <meta name="description" content="Learn more about our company and team" />
      </Head>

      <div className="about-page">
        <section className="hero">
          <h1>About Our Company</h1>
          <p>We are passionate about creating amazing web experiences using Next.js and modern technologies.</p>
        </section>

        <section className="mission">
          <h2>Our Mission</h2>
          <p>To build fast, scalable, and user-friendly web applications that make a difference in people's lives.</p>

          <div className="values">
            <div className="value">
              <h3>Innovation</h3>
              <p>We constantly explore new technologies and approaches</p>
            </div>
            <div className="value">
              <h3>Quality</h3>
              <p>We never compromise on code quality and user experience</p>
            </div>
            <div className="value">
              <h3>Collaboration</h3>
              <p>We believe in the power of teamwork and open communication</p>
            </div>
          </div>
        </section>

        <section className="team">
          <h2>Meet Our Team</h2>
          {loading ? (
            <div className="loading">Loading team members...</div>
          ) : (
            <div className="team-grid">
              {team.map(member => (
                <div key={member.id} className="team-member">
                  <div className="member-avatar">
                    {member.name.split(' ').map(n => n[0]).join('')}
                  </div>
                  <h3>{member.name}</h3>
                  <p className="role">{member.role}</p>
                  <p className="bio">{member.bio}</p>
                </div>
              ))}
            </div>
          )}
        </section>
      </div>

      <style jsx>{`
        .about-page {
          max-width: 1200px;
          margin: 0 auto;
          padding: 40px 20px;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }

        .hero {
          text-align: center;
          margin-bottom: 60px;
        }

        .hero h1 {
          font-size: 2.5rem;
          color: #333;
          margin-bottom: 20px;
        }

        .hero p {
          font-size: 1.2rem;
          color: #666;
          max-width: 600px;
          margin: 0 auto;
        }

        .mission {
          margin-bottom: 60px;
        }

        .mission h2 {
          font-size: 2rem;
          color: #333;
          margin-bottom: 20px;
        }

        .values {
          display: grid;
          grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
          gap: 30px;
          margin-top: 40px;
        }

        .value {
          text-align: center;
          padding: 30px;
          background: #f8f9fa;
          border-radius: 10px;
        }

        .value h3 {
          color: #0070f3;
          margin-bottom: 15px;
        }

        .team h2 {
          font-size: 2rem;
          color: #333;
          text-align: center;
          margin-bottom: 40px;
        }

        .loading {
          text-align: center;
          font-size: 1.1rem;
          color: #666;
        }

        .team-grid {
          display: grid;
          grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
          gap: 30px;
        }

        .team-member {
          text-align: center;
          padding: 30px;
          background: white;
          border-radius: 10px;
          box-shadow: 0 2px 10px rgba(0,0,0,0.1);
          transition: transform 0.3s, box-shadow 0.3s;
        }

        .team-member:hover {
          transform: translateY(-5px);
          box-shadow: 0 4px 20px rgba(0,0,0,0.15);
        }

        .member-avatar {
          width: 80px;
          height: 80px;
          border-radius: 50%;
          background: #0070f3;
          color: white;
          display: flex;
          align-items: center;
          justify-content: center;
          font-size: 1.5rem;
          font-weight: bold;
          margin: 0 auto 20px;
        }

        .team-member h3 {
          color: #333;
          margin-bottom: 10px;
        }

        .role {
          color: #0070f3;
          font-weight: 500;
          margin-bottom: 15px;
        }

        .bio {
          color: #666;
          line-height: 1.6;
        }
      `}</style>
    </>
  )
}

// 5. Blog List Page (pages/blog/index.js)
import Head from 'next/head'
import Link from 'next/link'
import { useState, useEffect } from 'react'

// Mock blog posts data
const blogPosts = [
  {
    id: 1,
    title: 'Getting Started with Next.js',
    excerpt: 'Learn the basics of Next.js and how to build your first application.',
    author: 'John Doe',
    date: '2024-01-15',
    tags: ['Next.js', 'React', 'Tutorial'],
    readTime: '5 min'
  },
  {
    id: 2,
    title: 'Server-Side Rendering vs Static Site Generation',
    excerpt: 'Understanding the differences between SSR and SSG in Next.js.',
    author: 'Jane Smith',
    date: '2024-01-10',
    tags: ['SSR', 'SSG', 'Performance'],
    readTime: '8 min'
  },
  {
    id: 3,
    title: 'Building API Routes in Next.js',
    excerpt: 'Create powerful backend APIs using Next.js API routes.',
    author: 'Mike Johnson',
    date: '2024-01-05',
    tags: ['API', 'Backend', 'Full-stack'],
    readTime: '10 min'
  }
]

export default function BlogListPage() {
  const [posts, setPosts] = useState([])
  const [filteredPosts, setFilteredPosts] = useState([])
  const [selectedTag, setSelectedTag] = useState('all')

  useEffect(() => {
    // Simulate fetching blog posts
    setTimeout(() => {
      setPosts(blogPosts)
      setFilteredPosts(blogPosts)
    }, 500)
  }, [])

  useEffect(() => {
    if (selectedTag === 'all') {
      setFilteredPosts(posts)
    } else {
      setFilteredPosts(posts.filter(post => post.tags.includes(selectedTag)))
    }
  }, [selectedTag, posts])

  const getAllTags = () => {
    const tags = ['all', ...new Set(posts.flatMap(post => post.tags))]
    return tags
  }

  return (
    <>
      <Head>
        <title>Blog - My Next.js App</title>
        <meta name="description" content="Read our latest articles about web development" />
      </Head>

      <div className="blog-container">
        <header className="blog-header">
          <h1>Our Blog</h1>
          <p>Insights, tutorials, and news about web development</p>
        </header>

        <div className="blog-content">
          <aside className="sidebar">
            <div className="tags-section">
              <h3>Filter by Tags</h3>
              <div className="tags">
                {getAllTags().map(tag => (
                  <button
                    key={tag}
                    className={`tag ${selectedTag === tag ? 'active' : ''}`}
                    onClick={() => setSelectedTag(tag)}
                  >
                    {tag}
                  </button>
                ))}
              </div>
            </div>

            <div className="recent-posts">
              <h3>Recent Posts</h3>
              <ul>
                {posts.slice(0, 3).map(post => (
                  <li key={post.id}>
                    <Link href={`/blog/${post.id}`}>
                      <a>{post.title}</a>
                    </Link>
                  </li>
                ))}
              </ul>
            </div>
          </aside>

          <main className="blog-main">
            {filteredPosts.length === 0 ? (
              <div className="no-posts">
                <h3>No posts found</h3>
                <p>Try selecting a different tag.</p>
              </div>
            ) : (
              <div className="posts-grid">
                {filteredPosts.map(post => (
                  <article key={post.id} className="post-card">
                    <Link href={`/blog/${post.id}`}>
                      <a>
                        <div className="post-meta">
                          <span className="date">{post.date}</span>
                          <span className="read-time">{post.readTime} read</span>
                        </div>
                        <h2>{post.title}</h2>
                        <p className="excerpt">{post.excerpt}</p>
                        <div className="post-footer">
                          <span className="author">By {post.author}</span>
                          <div className="tags">
                            {post.tags.map(tag => (
                              <span key={tag} className="tag">{tag}</span>
                            ))}
                          </div>
                        </div>
                      </a>
                    </Link>
                  </article>
                ))}
              </div>
            )}
          </main>
        </div>
      </div>

      <style jsx>{`
        .blog-container {
          max-width: 1200px;
          margin: 0 auto;
          padding: 40px 20px;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }

        .blog-header {
          text-align: center;
          margin-bottom: 60px;
        }

        .blog-header h1 {
          font-size: 2.5rem;
          color: #333;
          margin-bottom: 10px;
        }

        .blog-header p {
          font-size: 1.2rem;
          color: #666;
        }

        .blog-content {
          display: grid;
          grid-template-columns: 250px 1fr;
          gap: 40px;
        }

        .sidebar {
          position: sticky;
          top: 20px;
          height: fit-content;
        }

        .tags-section h3,
        .recent-posts h3 {
          color: #333;
          margin-bottom: 15px;
          font-size: 1.1rem;
        }

        .tags {
          display: flex;
          flex-wrap: wrap;
          gap: 8px;
        }

        .tag {
          padding: 5px 12px;
          background: #f0f0f0;
          border: 1px solid #ddd;
          border-radius: 20px;
          font-size: 0.9rem;
          cursor: pointer;
          transition: all 0.3s;
        }

        .tag:hover {
          background: #0070f3;
          color: white;
          border-color: #0070f3;
        }

        .tag.active {
          background: #0070f3;
          color: white;
          border-color: #0070f3;
        }

        .recent-posts ul {
          list-style: none;
          padding: 0;
        }

        .recent-posts li {
          margin-bottom: 10px;
        }

        .recent-posts a {
          color: #333;
          text-decoration: none;
          font-size: 0.95rem;
          line-height: 1.4;
        }

        .recent-posts a:hover {
          color: #0070f3;
        }

        .no-posts {
          text-align: center;
          padding: 60px 20px;
          color: #666;
        }

        .posts-grid {
          display: grid;
          grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
          gap: 30px;
        }

        .post-card {
          background: white;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            overflow: hidden;
            transition: transform 0.3s, box-shadow 0.3s;
        }

        .post-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 4px 20px rgba(0,0,0,0.15);
        }

        .post-card a {
          display: block;
          padding: 30px;
          text-decoration: none;
          color: inherit;
        }

        .post-meta {
          display: flex;
          justify-content: space-between;
          align-items: center;
          font-size: 0.9rem;
          color: #666;
          margin-bottom: 15px;
        }

        .post-card h2 {
          color: #333;
            font-size: 1.3rem;
            margin-bottom: 15px;
            line-height: 1.3;
        }

        .excerpt {
          color: #666;
          line-height: 1.6;
            margin-bottom: 20px;
        }

        .post-footer {
          display: flex;
          justify-content: space-between;
            align-items: center;
            font-size: 0.9rem;
        }

        .author {
            color: #666;
        }

        .post-footer .tags {
            display: flex;
            gap: 5px;
        }

        .post-footer .tag {
            font-size: 0.8rem;
            padding: 3px 8px;
        }

        @media (max-width: 768px) {
            .blog-content {
                grid-template-columns: 1fr;
            }

            .sidebar {
                position: static;
                margin-bottom: 40px;
            }

            .posts-grid {
                grid-template-columns: 1fr;
            }
        }
      `}</style>
    </>
  )
}

💻 Next.js API Routes and Data Fetching javascript

🟡 intermediate ⭐⭐⭐⭐

Building RESTful APIs with Next.js API routes, data fetching patterns, and server-side data handling

⏱️ 30 min 🏷️ nextjs, api, backend, database, swr
Prerequisites: Next.js basics, REST APIs, JavaScript async/await
// Next.js API Routes and Data Fetching

// 1. Simple API Route (pages/api/users.js)
export default function handler(req, res) {
  // Handle different HTTP methods
  switch (req.method) {
    case 'GET':
      return handleGet(req, res)
    case 'POST':
      return handlePost(req, res)
    case 'PUT':
      return handlePut(req, res)
    case 'DELETE':
      return handleDelete(req, res)
    default:
      res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE'])
      return res.status(405).end(`Method ${req.method} Not Allowed`)
  }
}

// Mock user data (in real app, this would come from a database)
let users = [
  { id: 1, name: 'John Doe', email: '[email protected]', age: 30 },
  { id: 2, name: 'Jane Smith', email: '[email protected]', age: 25 },
  { id: 3, name: 'Mike Johnson', email: '[email protected]', age: 35 }
]

let nextId = 4

function handleGet(req, res) {
  const { id } = req.query

  if (id) {
    // Get single user
    const user = users.find(u => u.id === parseInt(id))
    if (!user) {
      return res.status(404).json({ error: 'User not found' })
    }
    return res.status(200).json(user)
  } else {
    // Get all users with optional filtering
    const { minAge, maxAge } = req.query
    let filteredUsers = users

    if (minAge) {
      filteredUsers = filteredUsers.filter(u => u.age >= parseInt(minAge))
    }
    if (maxAge) {
      filteredUsers = filteredUsers.filter(u => u.age <= parseInt(maxAge))
    }

    return res.status(200).json({
      users: filteredUsers,
      total: filteredUsers.length
    })
  }
}

function handlePost(req, res) {
  try {
    const { name, email, age } = req.body

    // Validation
    if (!name || !email || !age) {
      return res.status(400).json({
        error: 'Missing required fields: name, email, age'
      })
    }

    if (typeof age !== 'number' || age < 0 || age > 150) {
      return res.status(400).json({
        error: 'Age must be a number between 0 and 150'
      })
    }

    // Check if email already exists
    if (users.some(u => u.email === email)) {
      return res.status(400).json({
        error: 'Email already exists'
      })
    }

    // Create new user
    const newUser = {
      id: nextId++,
      name,
      email,
      age
    }

    users.push(newUser)

    res.status(201).json({
      message: 'User created successfully',
      user: newUser
    })
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' })
  }
}

function handlePut(req, res) {
  try {
    const { id } = req.query
    const { name, email, age } = req.body

    if (!id) {
      return res.status(400).json({ error: 'User ID is required' })
    }

    const userIndex = users.findIndex(u => u.id === parseInt(id))
    if (userIndex === -1) {
      return res.status(404).json({ error: 'User not found' })
    }

    // Update user
    if (name) users[userIndex].name = name
    if (email) users[userIndex].email = email
    if (age !== undefined) users[userIndex].age = age

    res.status(200).json({
      message: 'User updated successfully',
      user: users[userIndex]
    })
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' })
  }
}

function handleDelete(req, res) {
  try {
    const { id } = req.query

    if (!id) {
      return res.status(400).json({ error: 'User ID is required' })
    }

    const userIndex = users.findIndex(u => u.id === parseInt(id))
    if (userIndex === -1) {
      return res.status(404).json({ error: 'User not found' })
    }

    const deletedUser = users.splice(userIndex, 1)[0]

    res.status(200).json({
      message: 'User deleted successfully',
      user: deletedUser
    })
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' })
  }
}

// 2. Advanced API Route with Database Integration (pages/api/products.js)
import { MongoClient, ObjectId } from 'mongodb'

const uri = process.env.MONGODB_URI
const client = new MongoClient(uri)

export default async function handler(req, res) {
  try {
    await client.connect()
    const database = client.db('myapp')
    const collection = database.collection('products')

    switch (req.method) {
      case 'GET':
        return await getProducts(collection, req, res)
      case 'POST':
        return await createProduct(collection, req, res)
      case 'PUT':
        return await updateProduct(collection, req, res)
      case 'DELETE':
        return await deleteProduct(collection, req, res)
      default:
        res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE'])
        return res.status(405).end(`Method ${req.method} Not Allowed`)
    }
  } catch (error) {
    console.error('Database error:', error)
    res.status(500).json({ error: 'Database connection failed' })
  } finally {
    await client.close()
  }
}

async function getProducts(collection, req, res) {
  const { page = 1, limit = 10, category, minPrice, maxPrice, sortBy = 'createdAt' } = req.query

  try {
    // Build query
    const query = {}
    if (category) query.category = category
    if (minPrice || maxPrice) {
      query.price = {}
      if (minPrice) query.price.$gte = parseFloat(minPrice)
      if (maxPrice) query.price.$lte = parseFloat(maxPrice)
    }

    // Build sort options
    const sortOptions = {}
    if (sortBy) {
      sortOptions[sortBy] = -1 // descending order
    }

    const skip = (parseInt(page) - 1) * parseInt(limit)

    const products = await collection
      .find(query)
      .sort(sortOptions)
      .skip(skip)
      .limit(parseInt(limit))
      .toArray()

    const total = await collection.countDocuments(query)

    res.status(200).json({
      products,
      pagination: {
        currentPage: parseInt(page),
        totalPages: Math.ceil(total / parseInt(limit)),
        totalItems: total,
        itemsPerPage: parseInt(limit)
      }
    })
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch products' })
  }
}

async function createProduct(collection, req, res) {
  try {
    const { name, description, price, category, inStock = true, images = [] } = req.body

    // Validation
    if (!name || !price || !category) {
      return res.status(400).json({
        error: 'Missing required fields: name, price, category'
      })
    }

    const product = {
      name,
      description,
      price: parseFloat(price),
      category,
      inStock,
      images,
      createdAt: new Date(),
      updatedAt: new Date()
    }

    const result = await collection.insertOne(product)

    res.status(201).json({
      message: 'Product created successfully',
      product: { ...product, _id: result.insertedId }
    })
  } catch (error) {
    res.status(500).json({ error: 'Failed to create product' })
  }
}

// 3. Client-side Data Fetching Component (components/UserList.js)
import { useState, useEffect } from 'react'

export default function UserList() {
  const [users, setUsers] = useState([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [filters, setFilters] = useState({
    minAge: '',
    maxAge: ''
  })

  useEffect(() => {
    fetchUsers()
  }, [filters.minAge, filters.maxAge])

  async function fetchUsers() {
    try {
      setLoading(true)
      setError(null)

      const queryParams = new URLSearchParams()
      if (filters.minAge) queryParams.append('minAge', filters.minAge)
      if (filters.maxAge) queryParams.append('maxAge', filters.maxAge)

      const response = await fetch(`/api/users?${queryParams}`)
      const data = await response.json()

      if (!response.ok) {
        throw new Error(data.error || 'Failed to fetch users')
      }

      setUsers(data.users)
    } catch (err) {
      setError(err.message)
    } finally {
      setLoading(false)
    }
  }

  const handleFilterChange = (field, value) => {
    setFilters(prev => ({ ...prev, [field]: value }))
  }

  const deleteUser = async (userId) => {
    if (!confirm('Are you sure you want to delete this user?')) {
      return
    }

    try {
      const response = await fetch(`/api/users?id=${userId}`, {
        method: 'DELETE'
      })

      const data = await response.json()

      if (!response.ok) {
        throw new Error(data.error || 'Failed to delete user')
      }

      // Remove user from local state
      setUsers(prev => prev.filter(user => user.id !== userId))
      alert('User deleted successfully')
    } catch (err) {
      alert(`Error deleting user: ${err.message}`)
    }
  }

  if (loading) {
    return <div className="loading">Loading users...</div>
  }

  if (error) {
    return <div className="error">Error: {error}</div>
  }

  return (
    <div className="user-list">
      <h2>User Management</h2>

      {/* Filters */}
      <div className="filters">
        <h3>Filters</h3>
        <div className="filter-group">
          <label>
            Min Age:
            <input
              type="number"
              value={filters.minAge}
              onChange={(e) => handleFilterChange('minAge', e.target.value)}
              min="0"
              max="150"
            />
          </label>
          <label>
            Max Age:
            <input
              type="number"
              value={filters.maxAge}
              onChange={(e) => handleFilterChange('maxAge', e.target.value)}
              min="0"
              max="150"
            />
          </label>
        </div>
      </div>

      {/* Users Table */}
      <div className="table-container">
        <table>
          <thead>
            <tr>
              <th>ID</th>
              <th>Name</th>
              <th>Email</th>
              <th>Age</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            {users.map(user => (
              <tr key={user.id}>
                <td>{user.id}</td>
                <td>{user.name}</td>
                <td>{user.email}</td>
                <td>{user.age}</td>
                <td>
                  <button
                    onClick={() => deleteUser(user.id)}
                    className="delete-btn"
                  >
                    Delete
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {users.length === 0 && (
        <div className="no-users">
          <p>No users found matching the current filters.</p>
        </div>
      )}

      <style jsx>{`
        .user-list {
          max-width: 1200px;
          margin: 0 auto;
          padding: 20px;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }

        h2 {
          color: #333;
          margin-bottom: 30px;
        }

        .loading, .error {
          text-align: center;
          padding: 40px;
          font-size: 1.1rem;
        }

        .error {
          color: #e74c3c;
        }

        .filters {
          background: #f8f9fa;
          padding: 20px;
          border-radius: 8px;
          margin-bottom: 30px;
        }

        .filters h3 {
          margin-top: 0;
          margin-bottom: 15px;
          color: #333;
        }

        .filter-group {
          display: flex;
          gap: 20px;
          flex-wrap: wrap;
        }

        .filter-group label {
          display: flex;
          flex-direction: column;
          font-weight: 500;
          color: #555;
        }

        .filter-group input {
          margin-top: 5px;
          padding: 8px;
          border: 1px solid #ddd;
          border-radius: 4px;
          font-size: 14px;
        }

        .table-container {
          overflow-x: auto;
          background: white;
          border-radius: 8px;
          box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        table {
          width: 100%;
          border-collapse: collapse;
        }

        th, td {
          padding: 12px;
          text-align: left;
          border-bottom: 1px solid #eee;
        }

        th {
          background-color: #f8f9fa;
          font-weight: 600;
          color: #333;
        }

        tr:hover {
          background-color: #f8f9fa;
        }

        .delete-btn {
          background-color: #e74c3c;
          color: white;
          border: none;
          padding: 6px 12px;
          border-radius: 4px;
          cursor: pointer;
          font-size: 12px;
          transition: background-color 0.3s;
        }

        .delete-btn:hover {
          background-color: #c0392b;
        }

        .no-users {
          text-align: center;
          padding: 40px;
          color: #666;
        }

        @media (max-width: 768px) {
          .filter-group {
            flex-direction: column;
          }

          .table-container {
            font-size: 14px;
          }

          th, td {
            padding: 8px;
          }
        }
      `}</style>
    </div>
  )
}

// 4. SWR Hook for Data Fetching (hooks/useUsers.js)
import useSWR from 'swr'

const fetcher = async (url) => {
  const response = await fetch(url)
  const data = await response.json()

  if (!response.ok) {
    throw new Error(data.error || 'An error occurred while fetching the data.')
  }

  return data
}

export function useUsers(filters = {}) {
  const queryParams = new URLSearchParams()

  if (filters.minAge) queryParams.append('minAge', filters.minAge)
  if (filters.maxAge) queryParams.append('maxAge', filters.maxAge)

  const url = queryParams.toString() ? `/api/users?${queryParams}` : '/api/users'

  const { data, error, isLoading, mutate } = useSWR(url, fetcher, {
    revalidateOnFocus: false,
    dedupingInterval: 5000
  })

  return {
    users: data?.users || [],
    total: data?.total || 0,
    isLoading,
    error,
    mutate
  }
}

export function useUser(id) {
  const { data, error, isLoading, mutate } = useSWR(
    id ? `/api/users?id=${id}` : null,
    fetcher
  )

  return {
    user: data,
    isLoading,
    error,
    mutate
  }
}

// 5. Custom Hook with SWR (hooks/useUserActions.js)
import { useUsers } from './useUsers'

export function useUserActions() {
  const { mutate } = useUsers()

  const createUser = async (userData) => {
    try {
      const response = await fetch('/api/users', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(userData),
      })

      const data = await response.json()

      if (!response.ok) {
        throw new Error(data.error || 'Failed to create user')
      }

      // Update local cache with new user
      mutate(currentData => ({
        ...currentData,
        users: [...(currentData?.users || []), data.user],
        total: (currentData?.total || 0) + 1
      }), false)

      return data
    } catch (error) {
      throw error
    }
  }

  const updateUser = async (id, userData) => {
    try {
      const response = await fetch(`/api/users?id=${id}`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(userData),
      })

      const data = await response.json()

      if (!response.ok) {
        throw new Error(data.error || 'Failed to update user')
      }

      // Update local cache
      mutate(currentData => ({
        ...currentData,
        users: (currentData?.users || []).map(user =>
          user.id === parseInt(id) ? data.user : user
        )
      }), false)

      return data
    } catch (error) {
      throw error
    }
  }

  const deleteUser = async (id) => {
    try {
      const response = await fetch(`/api/users?id=${id}`, {
        method: 'DELETE',
      })

      const data = await response.json()

      if (!response.ok) {
        throw new Error(data.error || 'Failed to delete user')
      }

      // Update local cache
      mutate(currentData => ({
        ...currentData,
        users: (currentData?.users || []).filter(user => user.id !== parseInt(id)),
        total: (currentData?.total || 0) - 1
      }), false)

      return data
    } catch (error) {
      throw error
    }
  }

  return {
    createUser,
    updateUser,
    deleteUser
  }
}

💻 Next.js Advanced Features javascript

🔴 complex ⭐⭐⭐⭐⭐

Advanced Next.js concepts including SSR, SSG, ISR, authentication, middleware, and deployment

⏱️ 40 min 🏷️ nextjs, ssr, ssg, auth, performance
Prerequisites: Advanced Next.js, React hooks, Authentication concepts, MongoDB
// Next.js Advanced Features

// 1. Server-Side Rendering (SSR) with getServerSideProps (pages/posts/[id].js)
import { MongoClient, ObjectId } from 'mongodb'
import Head from 'next/head'
import Link from 'next/link'
import { useRouter } from 'next/router'

export default function PostPage({ post, relatedPosts }) {
  const router = useRouter()

  if (router.isFallback) {
    return <div>Loading...</div>
  }

  return (
    <>
      <Head>
        <title>{post.title} - My Blog</title>
        <meta name="description" content={post.excerpt} />
        <meta property="og:title" content={post.title} />
        <meta property="og:description" content={post.excerpt} />
        <meta property="og:image" content={post.coverImage} />
      </Head>

      <article className="post">
        <header className="post-header">
          <nav className="breadcrumb">
            <Link href="/">
              <a>Home</a>
            </Link>
            {' / '}
            <Link href="/blog">
              <a>Blog</a>
            </Link>
            {' / '}
            <span>{post.title}</span>
          </nav>

          <h1>{post.title}</h1>

          <div className="post-meta">
            <span className="author">By {post.author.name}</span>
            <span className="date">
              {new Date(post.createdAt).toLocaleDateString()}
            </span>
            <span className="read-time">{post.readTime} min read</span>
          </div>

          <div className="tags">
            {post.tags.map(tag => (
              <span key={tag} className="tag">{tag}</span>
            ))}
          </div>
        </header>

        {post.coverImage && (
          <div className="cover-image">
            <img src={post.coverImage} alt={post.title} />
          </div>
        )}

        <div
          className="post-content"
          dangerouslySetInnerHTML={{ __html: post.content }}
        />

        <footer className="post-footer">
          <div className="author-bio">
            <img
              src={post.author.avatar}
              alt={post.author.name}
              className="author-avatar"
            />
            <div className="author-info">
              <h4>About {post.author.name}</h4>
              <p>{post.author.bio}</p>
            </div>
          </div>
        </footer>
      </article>

      <section className="related-posts">
        <h2>Related Posts</h2>
        <div className="posts-grid">
          {relatedPosts.map(relatedPost => (
            <Link key={relatedPost.id} href={`/posts/${relatedPost.id}`}>
              <a>
                <div className="post-card">
                  {relatedPost.coverImage && (
                    <img
                      src={relatedPost.coverImage}
                      alt={relatedPost.title}
                      className="card-image"
                    />
                  )}
                  <div className="card-content">
                    <h3>{relatedPost.title}</h3>
                    <p>{relatedPost.excerpt}</p>
                    <div className="card-meta">
                      <span>{new Date(relatedPost.createdAt).toLocaleDateString()}</span>
                    </div>
                  </div>
                </div>
              </a>
            </Link>
          ))}
        </div>
      </section>

      <style jsx>{`
        .post {
          max-width: 800px;
          margin: 0 auto;
          padding: 40px 20px;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
          line-height: 1.6;
        }

        .breadcrumb {
          margin-bottom: 20px;
          font-size: 0.9rem;
          color: #666;
        }

        .breadcrumb a {
          color: #0070f3;
          text-decoration: none;
        }

        .post-header h1 {
          font-size: 2.5rem;
          color: #333;
          margin-bottom: 20px;
          line-height: 1.2;
        }

        .post-meta {
          display: flex;
          gap: 20px;
          margin-bottom: 20px;
          font-size: 0.9rem;
          color: #666;
        }

        .tags {
          display: flex;
          gap: 8px;
          margin-bottom: 40px;
        }

        .tag {
          padding: 4px 12px;
          background: #f0f0f0;
          border-radius: 20px;
          font-size: 0.8rem;
          color: #555;
        }

        .cover-image {
          margin-bottom: 40px;
        }

        .cover-image img {
          width: 100%;
          height: auto;
          border-radius: 8px;
        }

        .post-content {
          font-size: 1.1rem;
          line-height: 1.8;
        }

        .post-content h2 {
          margin-top: 40px;
          margin-bottom: 20px;
          color: #333;
        }

        .post-content p {
          margin-bottom: 20px;
        }

        .post-footer {
          margin-top: 60px;
          padding-top: 40px;
          border-top: 1px solid #eee;
        }

        .author-bio {
          display: flex;
          align-items: center;
          gap: 20px;
        }

        .author-avatar {
          width: 60px;
          height: 60px;
          border-radius: 50%;
          object-fit: cover;
        }

        .author-info h4 {
          margin: 0 0 5px 0;
          color: #333;
        }

        .author-info p {
          margin: 0;
          color: #666;
          font-size: 0.9rem;
        }

        .related-posts {
          margin-top: 80px;
        }

        .related-posts h2 {
          font-size: 1.8rem;
          color: #333;
          margin-bottom: 30px;
          text-align: center;
        }

        .posts-grid {
          display: grid;
          grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
          gap: 30px;
        }

        .post-card {
          background: white;
          border-radius: 8px;
          box-shadow: 0 2px 10px rgba(0,0,0,0.1);
          overflow: hidden;
          transition: transform 0.3s;
        }

        .post-card:hover {
          transform: translateY(-5px);
        }

        .post-card a {
          display: block;
          text-decoration: none;
          color: inherit;
        }

        .card-image {
          width: 100%;
          height: 200px;
          object-fit: cover;
        }

        .card-content {
          padding: 20px;
        }

        .card-content h3 {
          margin: 0 0 10px 0;
          color: #333;
          font-size: 1.1rem;
        }

        .card-content p {
          margin: 0 0 15px 0;
          color: #666;
          font-size: 0.9rem;
        }

        .card-meta {
          color: #888;
          font-size: 0.8rem;
        }
      `}</style>
    </>
  )
}

export async function getServerSideProps(context) {
  const { id } = context.params
  const { req } = context

  try {
    // Connect to database
    const client = new MongoClient(process.env.MONGODB_URI)
    await client.connect()
    const database = client.db('myblog')
    const postsCollection = database.collection('posts')

    // Fetch the specific post
    const post = await postsCollection.findOne({
      _id: new ObjectId(id),
      published: true
    })

    if (!post) {
      return {
        notFound: true
      }
    }

    // Convert ObjectId to string for JSON serialization
    post._id = post._id.toString()

    // Fetch author information
    const usersCollection = database.collection('users')
    const author = await usersCollection.findOne(
      { _id: new ObjectId(post.authorId) },
      { projection: { name: 1, avatar: 1, bio: 1 } }
    )

    post.author = author ? {
      ...author,
      _id: author._id.toString()
    } : { name: 'Anonymous' }

    // Fetch related posts
    const relatedPosts = await postsCollection
      .find({
        _id: { $ne: new ObjectId(id) },
        published: true,
        $or: [
          { category: post.category },
          { tags: { $in: post.tags } }
        ]
      })
      .sort({ createdAt: -1 })
      .limit(3)
      .toArray()

    // Convert ObjectIds to strings
    const formattedRelatedPosts = relatedPosts.map(relatedPost => ({
      ...relatedPost,
      _id: relatedPost._id.toString()
    }))

    await client.close()

    return {
      props: {
        post,
        relatedPosts: formattedRelatedPosts
      }
    }
  } catch (error) {
    console.error('Error fetching post:', error)
    return {
      props: {
        post: null,
        relatedPosts: []
      }
    }
  }
}

// 2. Static Site Generation (SSG) with getStaticProps (pages/docs/[slug].js)
import { promises as fs } from 'fs'
import { join } from 'path'
import matter from 'gray-matter'
import { serialize } from 'next-mdx-remote/serialize'

export default function DocumentationPage({ frontMatter, content, tableOfContents }) {
  return (
    <>
      <div className="docs-container">
        <nav className="table-of-contents">
          <h3>Table of Contents</h3>
          <ul>
            {tableOfContents.map((item, index) => (
              <li key={index}>
                <a href={`#${item.anchor}`}>{item.title}</a>
              </li>
            ))}
          </ul>
        </nav>

        <main className="docs-content">
          <h1>{frontMatter.title}</h1>

          <div className="meta">
            <span className="date">
              {new Date(frontMatter.date).toLocaleDateString()}
            </span>
            <span className="author">By {frontMatter.author}</span>
            <span className="reading-time">{frontMatter.readingTime} min read</span>
          </div>

          <div className="content">
            {content}
          </div>
        </main>
      </div>

      <style jsx>{`
        .docs-container {
          display: grid;
          grid-template-columns: 250px 1fr;
          max-width: 1200px;
          margin: 0 auto;
          padding: 40px 20px;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }

        .table-of-contents {
          position: sticky;
          top: 20px;
          height: fit-content;
        }

        .table-of-contents h3 {
          margin-bottom: 15px;
          color: #333;
          font-size: 1.1rem;
        }

        .table-of-contents ul {
          list-style: none;
          padding: 0;
        }

        .table-of-contents li {
          margin-bottom: 8px;
        }

        .table-of-contents a {
          color: #0070f3;
          text-decoration: none;
          font-size: 0.9rem;
          line-height: 1.4;
        }

        .table-of-contents a:hover {
          text-decoration: underline;
        }

        .docs-content {
          max-width: 800px;
        }

        .docs-content h1 {
          color: #333;
          margin-bottom: 10px;
          font-size: 2rem;
        }

        .meta {
          display: flex;
          gap: 20px;
          margin-bottom: 40px;
          font-size: 0.9rem;
          color: #666;
        }

        .content {
          line-height: 1.6;
        }

        .content h2 {
          color: #333;
          margin: 40px 0 20px 0;
          font-size: 1.5rem;
        }

        .content h3 {
          color: #333;
          margin: 30px 0 15px 0;
          font-size: 1.2rem;
        }

        .content p {
          margin-bottom: 20px;
        }

        .content pre {
          background: #f6f8fa;
          padding: 20px;
          border-radius: 8px;
          overflow-x: auto;
          margin-bottom: 20px;
        }

        .content code {
          background: #f6f8fa;
          padding: 2px 6px;
          border-radius: 4px;
          font-size: 0.9rem;
        }

        @media (max-width: 768px) {
          .docs-container {
            grid-template-columns: 1fr;
          }

          .table-of-contents {
            position: static;
            margin-bottom: 40px;
          }
        }
      `}</style>
    </>
  )
}

export async function getStaticPaths() {
  const postsDirectory = join(process.cwd(), 'docs')

  try {
    const fileNames = await fs.readdir(postsDirectory)
    const paths = fileNames
      .filter(name => name.endsWith('.mdx'))
      .map(name => ({
        params: {
          slug: name.replace(/\.mdx$/, '')
        }
      }))

    return {
      paths,
      fallback: false
    }
  } catch (error) {
    console.error('Error reading docs directory:', error)
    return {
      paths: [],
      fallback: false
    }
  }
}

export async function getStaticProps({ params }) {
  const { slug } = params
  const fullPath = join(process.cwd(), 'docs', `${slug}.mdx`)

  try {
    const fileContents = await fs.readFile(fullPath, 'utf8')
    const { data, content } = matter(fileContents)

    // Generate table of contents
    const headings = content.match(/^###\s+(.+)$/gm) || []
    const tableOfContents = headings.map((heading, index) => {
      const title = heading.replace(/^###\s+/, '')
      const anchor = title.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '')
      return { title, anchor }
    })

    // Serialize MDX content
    const mdxSource = await serialize(content)

    return {
      props: {
        frontMatter: data,
        content: mdxSource,
        tableOfContents
      }
    }
  } catch (error) {
    console.error(`Error reading file ${fullPath}:`, error)
    return {
      props: {
        frontMatter: {},
        content: null,
        tableOfContents: []
      }
    }
  }
}

// 3. Authentication Middleware (middleware.js)
import { NextResponse } from 'next/server'
import { getToken } from 'next-auth/jwt'

// This function can be marked `async` if using `await` inside
export async function middleware(req) {
  const { pathname } = req.next

  // Skip middleware for API routes and static files
  if (pathname.startsWith('/api/') ||
      pathname.startsWith('/_next/') ||
      pathname.includes('.')) {
    return NextResponse.next()
  }

  // Public routes that don't require authentication
  const publicRoutes = ['/login', '/register', '/forgot-password']
  if (publicRoutes.includes(pathname)) {
    return NextResponse.next()
  }

  // Protected routes that require authentication
  const protectedRoutes = ['/dashboard', '/profile', '/settings', '/admin']
  const isProtectedRoute = protectedRoutes.some(route =>
    pathname.startsWith(route)
  )

  if (isProtectedRoute) {
    const token = await getToken({ req })

    if (!token) {
      // Redirect to login page with return URL
      const loginUrl = new URL('/login', req.url)
      loginUrl.searchParams.set('returnUrl', pathname)
      return NextResponse.redirect(loginUrl)
    }

    // Admin routes require admin role
    if (pathname.startsWith('/admin')) {
      try {
        // Verify admin role (this would involve checking the token payload)
        // const user = await verifyToken(token)
        // if (user.role !== 'admin') {
        //   return NextResponse.redirect('/unauthorized')
        // }
      } catch (error) {
        return NextResponse.redirect('/login')
      }
    }
  }

  return NextResponse.next()
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
}

// 4. Custom App Component (pages/_app.js)
import { SessionProvider } from 'next-auth/react'
import { QueryClient, QueryClientProvider } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'
import '../styles/globals.css'

// Create a client
function getQueryClient() {
  if (typeof window === 'undefined') {
    // Server: always make a new query client
    return new QueryClient({
      defaultOptions: {
        queries: {
          staleTime: 60 * 1000,
          refetchOnWindowFocus: false,
        },
      },
    })
  } else {
    // Browser: make a new query client if we don't already have one
    if (!global.queryClient) global.queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          staleTime: 60 * 1000,
          refetchOnWindowFocus: false,
        },
      },
    })
    return global.queryClient
  }
}

export default function App({
  Component,
  pageProps: { session, ...pageProps },
}) {
  const queryClient = getQueryClient()

  return (
    <SessionProvider session={session}>
      <QueryClientProvider client={queryClient}>
        <Component {...pageProps} />
        {process.env.NODE_ENV === 'development' && (
          <ReactQueryDevtools initialIsOpen={false} />
        )}
      </QueryClientProvider>
    </SessionProvider>
  )
}

// 5. Error Handling (pages/_error.js)
import Head from 'next/head'
import Link from 'next/link'

export default function Error({ statusCode, err }) {
  const title = statusCode === 404 ? 'Page Not Found' : 'An Error Occurred'
  const message = getErrorMessage(statusCode, err)

  return (
    <>
      <Head>
        <title>
          {statusCode}: {title}
        </title>
      </Head>

      <div className="error-container">
        <div className="error-content">
          <h1>{statusCode}</h1>
          <h2>{title}</h2>
          <p>{message}</p>

          <div className="error-actions">
            <Link href="/">
              <a className="btn-primary">Go Home</a>
            </Link>
            <button onClick={() => window.history.back()} className="btn-secondary">
              Go Back
            </button>
          </div>

          {process.env.NODE_ENV === 'development' && err && (
            <details className="error-details">
              <summary>Error Details</summary>
              <pre>{err.stack}</pre>
            </details>
          )}
        </div>
      </div>

      <style jsx>{`
        .error-container {
          min-height: 100vh;
          display: flex;
          align-items: center;
          justify-content: center;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
          background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        }

        .error-content {
          text-align: center;
          background: white;
          padding: 60px;
          border-radius: 12px;
          box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
          max-width: 500px;
        }

        .error-content h1 {
          font-size: 6rem;
          font-weight: bold;
          color: #e74c3c;
          margin: 0 0 20px 0;
        }

        .error-content h2 {
          font-size: 1.8rem;
          color: #333;
          margin: 0 0 20px 0;
        }

        .error-content p {
          color: #666;
          margin: 0 0 30px 0;
          line-height: 1.6;
        }

        .error-actions {
          display: flex;
          gap: 15px;
          justify-content: center;
        }

        .btn-primary,
        .btn-secondary {
          padding: 12px 24px;
          border: none;
          border-radius: 6px;
          font-size: 1rem;
          font-weight: 500;
          cursor: pointer;
          transition: all 0.3s;
          text-decoration: none;
          display: inline-block;
        }

        .btn-primary {
          background-color: #0070f3;
          color: white;
        }

        .btn-primary:hover {
          background-color: #0051cc;
        }

        .btn-secondary {
          background-color: #6c757d;
          color: white;
        }

        .btn-secondary:hover {
          background-color: #5a6268;
        }

        .error-details {
          margin-top: 30px;
          text-align: left;
        }

        .error-details summary {
          cursor: pointer;
          font-weight: 500;
          margin-bottom: 10px;
          padding: 10px;
          background: #f8f9fa;
          border-radius: 4px;
        }

        .error-details pre {
          background: #f1f3f4;
          padding: 15px;
          border-radius: 4px;
          overflow-x: auto;
          font-size: 0.8rem;
          color: #333;
        }

        @media (max-width: 768px) {
          .error-content {
            margin: 20px;
            padding: 40px 20px;
          }

          .error-content h1 {
            font-size: 4rem;
          }

          .error-content h2 {
            font-size: 1.5rem;
          }

          .error-actions {
            flex-direction: column;
          }
        }
      `}</style>
    </>
  )
}

function getErrorMessage(statusCode, err) {
  switch (statusCode) {
    case 404:
      return 'Sorry, the page you are looking for could not be found.'
    case 500:
      return 'Sorry, something went wrong on our end. Please try again later.'
    default:
      return 'An unexpected error occurred.'
  }
}