NextJS
Advanced FAQs

Here are some advanced interview questions on Next.js, along with detailed answers and real-world examples to help you understand the concepts better:

1. What is the difference between Static Generation and Server-Side Rendering in Next.js, and when would you choose one over the other?

Answer:

Static Generation (SSG) is the process of generating HTML at build time. The HTML is generated once and served to all users. It’s suitable for pages where content does not change frequently, such as blog posts or documentation.

Server-Side Rendering (SSR) generates HTML on each request. This is suitable for pages where content changes frequently or is user-specific, like dashboards or real-time data.

When to choose SSG:

  • When the content is known at build time and doesn’t change frequently (e.g., blogs, documentation).
  • When you want faster loading times for users, as the page is served as static HTML.

When to choose SSR:

  • When content is dynamic or user-specific and must be fetched fresh on each request (e.g., user dashboards, admin panels).

Example:

  • A blog page can be statically generated using getStaticProps, whereas a user profile page would likely use getServerSideProps to fetch user-specific data on each request.

2. How does Next.js handle API routes, and what are some common use cases?

Answer:

Next.js allows you to create API routes that can be used to build your backend directly within the Next.js application. API routes are created in the pages/api directory. Each file in this directory corresponds to an API endpoint.

Common use cases:

  • Form Handling: Submitting forms and processing data without needing a separate backend.
  • Fetching External APIs: Making server-side API calls and returning the data to the client.
  • Authentication: Handling user authentication and authorization processes.

Example:

// pages/api/users.js
export default async function handler(req, res) {
  if (req.method === 'GET') {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    const users = await response.json();
    res.status(200).json(users);
  } else {
    res.setHeader('Allow', ['GET']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

In this example, we create an API endpoint that fetches a list of users from an external API.

3. Explain the concept of Incremental Static Regeneration (ISR) and provide an example of how to implement it.

Answer:

Incremental Static Regeneration (ISR) allows you to update static pages after you’ve built your site. It enables you to specify a revalidation time, after which Next.js will regenerate the page in the background when a request comes in.

Implementation: You can implement ISR by using getStaticProps along with the revalidate option.

Example:

// pages/products/[id].js
export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/products/${params.id}`);
  const product = await res.json();
 
  return {
    props: {
      product,
    },
    revalidate: 10, // Re-generate the page every 10 seconds
  };
}
 
const ProductPage = ({ product }) => (
  <div>
    <h1>{product.name}</h1>
    <p>{product.description}</p>
  </div>
);
 
export default ProductPage;

In this example, the product page will be revalidated and regenerated every 10 seconds if it receives a request.

4. How can you optimize performance in a Next.js application?

Answer:

There are several strategies to optimize performance in a Next.js application:

  • Static Generation: Use getStaticProps and getStaticPaths to pre-render pages at build time for fast loading.
  • Image Optimization: Use the Next.js Image component to automatically optimize images (lazy loading, resizing).
  • Code Splitting: Next.js automatically splits your code into smaller chunks, loading only what is necessary for the initial render.
  • Dynamic Imports: Use dynamic imports to load components or libraries only when needed.
  • Prefetching: Next.js prefetches linked pages in the background, speeding up navigation.

Example: Using the Image component

import Image from 'next/image';
 
const Profile = () => (
  <div>
    <h1>My Profile</h1>
    <Image
      src="/profile.jpg"
      alt="Profile Picture"
      width={500}
      height={500}
      priority // loads this image during the initial load
    />
  </div>
);
 
export default Profile;

5. What is the role of middleware in Next.js, and how can it be used for authentication?

Answer:

Middleware in Next.js runs before a request is completed and can be used to execute code for various tasks such as authentication, logging, and request modification.

You can use middleware to check if a user is authenticated before allowing access to certain routes.

Example:

// middleware.js
import { NextResponse } from 'next/server';
 
export function middleware(req) {
  const token = req.cookies.get('auth-token');
 
  // Check for authentication
  if (!token && req.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect('/login');
  }
 
  return NextResponse.next();
}
 
// config.js
export const config = {
  matcher: ['/dashboard/:path*'], // apply to all routes under /dashboard
};

In this example, the middleware checks for an authentication token in cookies before allowing access to the /dashboard routes.

6. How do you implement Internationalization (i18n) in a Next.js application?

Answer:

Next.js provides built-in support for internationalization (i18n) through configuration in next.config.js. You can define locales, default locale, and domain-specific locales.

Implementation:

  1. Configuration in next.config.js:
// next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'de'],
    defaultLocale: 'en-US',
  },
};
  1. Using Next.js i18n Router: You can use the router to navigate between different locales.

Example:

import { useRouter } from 'next/router';
 
const LanguageSwitcher = () => {
  const router = useRouter();
 
  const changeLanguage = (locale) => {
    router.push(router.pathname, router.asPath, { locale });
  };
 
  return (
    <div>
      <button onClick={() => changeLanguage('fr')}>French</button>
      <button onClick={() => changeLanguage('de')}>German</button>
    </div>
  );
};
 
export default LanguageSwitcher;

7. Explain the use of getStaticPaths and how it works with dynamic routes in Next.js.

Answer:

getStaticPaths is used in conjunction with getStaticProps to generate dynamic routes at build time. It defines the dynamic paths that will be pre-rendered.

Implementation: You specify which paths you want to pre-render based on data fetched from an API or database.

Example:

// pages/products/[id].js
export async function getStaticPaths() {
  const res = await fetch('https://api.example.com/products');
  const products = await res.json();
 
  const paths = products.map((product) => ({
    params: { id: product.id.toString() },
  }));
 
  return { paths, fallback: false }; // or true for ISR
}
 
export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/products/${params.id}`);
  const product = await res.json();
 
  return {
    props: {
      product,
    },
  };
}
 
const ProductPage = ({ product }) => (
  <div>
    <h1>{product.name}</h1>
    <p>{product.description}</p>
  </div>
);
 
export default ProductPage;

In this example, getStaticPaths fetches all products to generate paths for each product dynamically.

8. How do you handle error pages in Next.js, and how can you create custom error handling logic?

Answer:

Next.js provides a default error handling page for 404 and 500 errors, but you can create custom error pages by adding a 404.js or 500.js file in the pages directory.

Custom Error Page Example:

// pages/404.js
const Custom404 = () => {
  return <h1>404 - Page Not Found</h1>;
};
 
export default Custom404;

Error Handling Logic: For more advanced error handling (e.g., for API routes), you can use try-catch blocks and return appropriate responses.

Example:

// pages/api/data.js
export default async function handler(req, res) {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error('Failed to fetch data');
    }
    const data = await response.json();
    res.status(200).json(data);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
}

In this example, if the fetch fails, a 500 error response is returned with a custom error message.


Conclusion

These advanced interview questions on Next.js cover a range of concepts from

routing and data fetching to performance optimization and internationalization. By understanding these concepts, you can effectively leverage Next.js to build robust applications.