🚀 This repository aimed to contains 500 nextjs interview questions & answers with exmample.
No | Contents |
---|---|
1 | Common |
2 | Pages Router |
3 | App Router |
-
Next.js is a React framework for building full-stack web applications.
-
Using command
npx create-next-app@latest
-
It contains React components that are automatically routed based on their file name.
-
Routing based on the file structure in the
pages or app
directory. -
- Server Side Rendering (SSR): Next.js allows rendering React components on the server before sending them to the client, improving performance and SEO.
- Static Site Generation (SSG): It pre-renders pages at build time, useful for blogs or e-commerce sites.
- API Routes: You can build a backend using API routes in the same codebase without needing an external server.
- File Based Routing: Next.js automatically creates routes based on the file structure inside the pages directory.
- Client Side Rendering (CSR): Like React, Next.js also supports traditional client-side rendering.
- Incremental Side Rendering:
- Image Optimization: Built-in image optimization capabilities that reduce image sizes and enhance loading times.
- Automatic Code Splitting: Next.js splits the code into smaller bundles, which are loaded only when required, improving performance.
- TypeScript Support: Native support for TypeScript, enabling strict typing and better developer experience.
- Incremental Static Regeneration (ISR): Pages can be statically generated at runtime and updated incrementally.
- Fast Refresh: Provides an instant feedback loop while coding, similar to React's hot reloading.
-
Feature Next.js React.js Rendering Supports Server-Side Rendering (SSR), Static Site Generation (SSG), and Client-Side Rendering (CSR). Only supports Client-Side Rendering (CSR) by default. Routing Built-in file-based routing system. Automatically generates routes based on the folder structure. No built-in routing. Requires libraries like React Router. SEO Excellent for SEO as it supports SSR and SSG, allowing pre-rendered content to be indexed by search engines. Limited SEO capabilities due to client-side rendering. Additional work is needed for SEO optimization. Performance Faster initial page load due to SSR, automatic code splitting, and static generation. May have slower page loads for large apps since everything is rendered on the client. Configuration Minimal configuration required. Comes with SSR, SSG, and routing out of the box. Requires manual setup for SSR, routing, and other advanced features. Learning Curve Slightly steeper due to built-in advanced features like SSR, SSG, and API routes. Easier to learn initially, but requires additional tools for SSR and routing. API Routes Built-in API routes that can handle backend logic within the same project. No support for API routes; requires external tools for backend development. Code Splitting Automatically splits code into smaller bundles, loading only what's needed for a specific page. Requires manual code splitting or use of lazy loading to optimize performance. Deployment Optimized for easy deployment on platforms like Vercel (creators of Next.js) and supports serverless functions. Deployment typically requires additional configuration for optimized hosting and SSR. Image Optimization Has a built-in Image component for automatic image resizing and optimization. Does not provide image optimization; developers need third-party libraries for that. -
Client-side rendering (CSR) means that the browser fetches the JavaScript and renders the page on the client side, while server-side rendering (SSR) means that the server generates the HTML and sends it to the client.
-
A component for client side navigation between pages.
import Link from "next/link"; <Link href="/">Home</Link> <Link href="/about">About</Link> <Link href="/contact" prefetch={false}>Contact</Link>
-
A hook that allows access to the router object and perform navigation. The
useRouter
hook allows you to programmatically change routes inside client components. -
The
push
method adds a new entry to the browser's history stack, whilereplace
replaces the current entry in the history stack.const router = useRouter(); // Pushes a new route router.push("/new-route"); // Replaces the current route router.replace("/new-route");
-
Using useRouter() hook.
const router = userRouter(); function handleClick() { router.push(`/path`); } <button onClick={handleClick}>Go There</button>;
router.push(href: string, { scroll: boolean })
-
By adding a tsconfig.json file.
-
A feature to create API endpoints in the
pages/api
orapp/api
directory. It allow you to create custom request handlers for a given route using the Web Request and Response APIs. -
A folder for static assets to be served from the root URL.
public/ ├── favicon.ico |── robots.txt |── images/ | └── profile.jpg
-
A feature to load components or modules dynamically.
const ComponentA = dynamic(() => import("../components/A")); const ComponentB = dynamic(() => import("../components/B"));
-
By adding them to
.env.local
and accessing viaprocess.env.
-
Port 3000.
-
"scripts": { "dev": "next dev -p 8080", // for dev "start": "next start -p 8080" // for prod },
-
A feature for quick feedback when editing React components.
-
A configuration file to customize Next.js settings.
// @ts-check /** @type {import('next').NextConfig} */ const nextConfig = { /* config options here */ }; module.exports = nextConfig;
-
Using CSS modules with a
.module.css
file extension.// styles.module.css .example { color: red; } // Component.js import styles from './styles.module.css'; export default function Component() { return <div className={styles.example}>Hello World!</div>; }
-
By importing CSS files in the _app.js file.
-
By installing Tailwind CSS and configuring it in the next.config.js file.
npm install tailwindcss postcss autoprefixer npx tailwindcss init -p
Then, add the following to your
tailwind.config.js
:module.exports = { content: [ "./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}", ], theme: { extend: {}, }, plugins: [], };
And import Tailwind CSS in your _app.js:
import "tailwindcss/tailwind.css";
-
Rendering pages on each request. If a page uses Server-side Rendering, the page HTML is generated on each request.
export async function getServerSideProps() { const res = await fetch("https://api.github.com/repos/vercel/next.js"); const repo = await res.json(); return { props: { repo } }; } export default function Page({ repo }) { return <p>{repo.stargazers_count} Stars</p>; }
-
Pre-rendering pages at build time. If a page uses Static Generation, the page HTML is generated at build time.
export async function getStaticProps() { const res = await fetch("https://api.github.com/repos/vercel/next.js"); const repo = await res.json(); return { props: { repo } }; } export default function Page({ repo }) { return <p>{repo.stargazers_count} Stars</p>; }
-
Static site generation (SSG) pre-renders at build time, server side rendering (SSR) pre-renders on each request.
-
Generating HTML for pages in advance, instead of on each request.
export async function getStaticProps() { const res = await fetch("https://api.github.com/repos/vercel/next.js"); const repo = await res.json(); return { props: { repo } }; } export default function Page({ repo }) { return <p>{repo.stargazers_count} Stars</p>; }
-
Incremental Static Regeneration is a technique in Next.js that allows you to update static pages at runtime without rebuilding the entire site. This feature introduces a seamless way to serve both static and dynamic content by revalidating and regenerating pages in the background.
export async function getStaticProps() { const res = await fetch("https://api.github.com/repos/vercel/next.js"); const repo = await res.json(); return { props: { repo }, revalidate: 1 }; }
-
A component that optimizes images for faster loading.
export default function Page() { return ( <Image src={profilePic} alt="Picture of the author" // width={500} automatically provided // height={500} automatically provided // blurDataURL="data:..." automatically provided // placeholder="blur" // Optional blur-up while loading /> ); }
-
By connecting the git/github repository to Vercel and deploying it.
-
There are a few ways you can handle redirects in Next.js. One of them is by configuring redirects in next.config.js.
module.exports = { async redirects() { return [ { source: "/about", destination: "/about-us", permanent: true, }, ]; }, };
-
A component for modifying the of a page.
import Head from "next/head"; <Head> <title>My page title</title> <meta name="viewport" content="initial-scale=1.0, width=device-width" /> </Head>;
-
To manage the document head for meta tags, title,description, og etc.
import Head from "next/head"; export default function Home() { return ( <div> <Head> <title>My page title</title> <meta name="description" content="My description" /> </Head> <h1>Hello World!</h1> </div> ); }
-
By configuring headers in next.config.js.
-
To export a static version of the Next.js app.
-
By using the built-in font optimization feature.
-
By using the next/font package to optimize and load custom fonts.
import { Inter } from "next/font/google"; const inter = Inter({ subsets: ["latin"] }); export default function Home() { return ( <main className={inter.className}> <h1>Hello World!</h1> </main> ); }
-
By adding a custom webpack configuration in next.config.js.
module.exports = { webpack: ( config, { buildId, dev, isServer, defaultLoaders, webpack } ) => { // Note: we provide webpack above so you should not `require` it // Perform customizations to webpack config config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//)); // Important: return the modified config return config; }, };
-
By adding a babel.config.js file.
-
A TypeScript declaration file for Next.js types.
-
To compose and apply multiple Next.js plugins.
-
By importing them in the _app.js file or using next-polyfill.
-
A feature that automatically determines if a page can be statically generated.
-
By configuring i18n settings in next.config.js.
module.exports = { i18n: { locales: ["en", "fr"], defaultLocale: "en", }, };
-
A development mode only feature for highlighting potential problems in an application. It helps to identify unsafe lifecycles, legacy API usage, and a number of other features.
module.exports = { reactStrictMode: true, };
Note: Since Next.js 13.5.1, Strict Mode is true by default with app router, so the above configuration is only necessary for pages. You can still disable Strict Mode by setting
reactStrictMode: false
. -
A single router instance accessible across the application.
-
The
next/script
component is used to load external scripts in a Next.js application. It provides features like loading scripts asynchronously, deferring execution, and controlling script loading behavior.import Script from "next/script"; export default function Page() { return ( <> <Script src="https://example.com/script.js" strategy="lazyOnload" /> <h1>Hello World!</h1> </> ); }
-
Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers or responding directly.
import { NextResponse } from "next/server"; // This function can be marked `async` if using `await` inside export function middleware(request) { return NextResponse.redirect(new URL("/home", request.url)); } // See "Matching Paths" below to learn more export const config = { matcher: "/about/:path*", };
-
A way to customize the server-side behavior, e.g., with Express.
import { createServer } from "http"; import { parse } from "url"; import next from "next"; const port = parseInt(process.env.PORT || "3000", 10); const dev = process.env.NODE_ENV !== "production"; const app = next({ dev }); const handle = app.getRequestHandler(); app.prepare().then(() => { createServer((req, res) => { const parsedUrl = parse(req.url, true); handle(req, res, parsedUrl); }).listen(port); console.log( `> Server listening at http://localhost:${port} as ${ dev ? "development" : process.env.NODE_ENV }` ); });
-
Using
useEffect
and fetch or any other data fetching library likeaxios
,fetch
orswr
by Next.js team.import { useState, useEffect } from "react"; function Profile() { const [data, setData] = useState(null); const [isLoading, setLoading] = useState(true); useEffect(() => { fetch("/api/profile") .then((res) => res.json()) .then((data) => { setData(data); setLoading(false); }); }, []); if (isLoading) return <p>Loading...</p>; if (!data) return <p>No profile data</p>; return ( <div> <h1>{data.name}</h1> <p>{data.bio}</p> </div> ); }
-
By installing Apollo Client or any other GraphQL client and configuring it in the _app.js file.
npm install @apollo/client graphql yarn add @apollo/client graphql
Then, set up Apollo Client in your _app.js:
import { ApolloClient, InMemoryCache, ApolloProvider, } from "@apollo/client"; const client = new ApolloClient({ uri: "https://your-graphql-endpoint.com/graphql", cache: new InMemoryCache(), }); function MyApp({ Component, pageProps }) { return ( <ApolloProvider client={client}> <Component {...pageProps} /> </ApolloProvider> ); } export default MyApp;
-
By adding files to the pages/api or app/api directory.
-
next-seo is a plugin for managing SEO metadata in Next.js applications, making it easier to set and manage meta tags, Open Graph tags, and other SEO-related elements.
import { DefaultSeo } from "next-seo"; function MyApp({ Component, pageProps }) { return ( <> <DefaultSeo title="My Next.js App" description="A description of my Next.js app" openGraph={{ type: "website", locale: "en_IE", url: "https://www.example.com/", site_name: "My Next.js App", }} /> <Component {...pageProps} /> </> ); } export default MyApp;
-
Using file-based routing in the pages or app directory.
-
By creating a next-i18next.config.js file and initializing it in the app.
// next-i18next.config.js module.exports = { i18n: { defaultLocale: "en", locales: ["en", "fr"], }, };
Then, initialize it in your app:
import { appWithTranslation } from "next-i18next"; import nextI18NextConfig from "../next-i18next.config"; function MyApp({ Component, pageProps }) { return <Component {...pageProps} />; } export default appWithTranslation(MyApp, nextI18NextConfig);
-
It disables server-side rendering for a dynamically imported component, ensuring it only loads on the client side.
import dynamic from "next/dynamic"; const DynamicComponent = dynamic(() => import("../components/hello"), { ssr: false, }); function Home() { return ( <section> <Header /> <DynamicComponent /> <Footer /> </section> ); } export default Home;
-
By using the next/script component to load the Google Analytics script.
import Script from "next/script"; export default function MyApp() { return ( <> <Script src={`https://www.googletagmanager.com/gtag/js?id=YOUR_TRACKING_ID`} strategy="afterInteractive" /> <Script id="google-analytics" strategy="afterInteractive"> {` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'YOUR_TRACKING_ID'); `} </Script> </> ); }
-
Using the Head component from next/head.
import Head from "next/head"; function IndexPage() { return ( <div> <Head> <title>My page title</title> <meta name="viewport" content="initial-scale=1.0, width=device-width" /> <meta property="og:title" content="My page title" key="title" /> </Head> <p>Hello world!</p> </div> ); } export default IndexPage;
-
To add a sitemap in Next.js app router, you can use the
next-sitemap
package. First, install it:npm install next-sitemap
Then, create a
next-sitemap.config.js
file in the root of your project and configure your sitemap options./** @type {import('next-sitemap').IConfig} */ module.exports = { siteUrl: process.env.SITE_URL || "https://example.com", generateRobotsTxt: true, // (optional) changefreq: "daily", // (optional) priority: 0.7, // (optional) sitemapSize: 7000, // (optional) exclude: ["/404", "/500"], // (optional) robotsTxtOptions: { policies: [ { userAgent: "*", allow: "/" }, { userAgent: "Googlebot", disallow: "/private" }, ], }, };
Finally, run the following command to generate the sitemap:
npx next-sitemap
This will create a
sitemap.xml
file in thepublic
directory of your Next.js app. -
By setting appropriate headers in the API route response.
export default function handler(req, res) { res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); res.setHeader("Access-Control-Allow-Headers", "Content-Type"); res.status(200).json({ message: "CORS enabled" }); }
-
By using the cookie package or next-cookies to read and write cookies in API routes or server-side functions.
import Cookies from "cookies"; export default function handler(req, res) { const cookies = new Cookies(req, res); cookies.set("token", "value", { httpOnly: true }); res.status(200).json({ message: "Cookie set" }); }
-
For dynamic importing of components with support for SSR.
import dynamic from "next/dynamic"; // Client-side only component const DynamicComponentWithNoSSR = dynamic( () => import("../components/hello"), { ssr: false, } ); function Home() { return ( <div> <Header /> <DynamicComponentWithNoSSR /> <Footer /> </div> ); } export default Home;
-
To consider security in Next.js app router, you can follow these best practices:
- Use HTTPS for all requests to ensure data is encrypted in transit.
- Implement authentication and authorization to protect sensitive routes.
- Sanitize user input to prevent XSS attacks.
- Use environment variables to store sensitive information like API keys.
- Regularly update dependencies to patch known vulnerabilities.
- Implement Content Security Policy (CSP) headers to mitigate XSS attacks.
- Use secure cookies with the
HttpOnly
andSecure
flags.
-
A hook provided by next-i18next for internationalization.
-
A framework for creating fast, mobile-friendly pages.
-
By adding export const config = { amp: true } to a page.
-
For image optimization and responsive images.
-
For client-side navigation between pages.
-
Pages are routes, components are reusable UI elements.
-
By placing them in the
public
directory, which is served at the root URL.public/ ├── images/ │ └── logo.png └── favicon.ico
You can access these files using
/images/logo.png
or/favicon.ico
. -
- Use static generation (SSG) for pages that can be pre-rendered.
- Implement incremental static regeneration (ISR) for dynamic content.
- Use the next/image component for optimized images.
- Enable code splitting and tree shaking.
- Use dynamic imports for large components.
- Optimize CSS with CSS modules or styled-components.
- Leverage caching strategies for API routes.
-
- Use HTTPS for secure communication.
- Implement authentication and authorization.
- Sanitize user input to prevent XSS attacks.
- Use environment variables for sensitive data.
- Regularly update dependencies to patch vulnerabilities.
- Implement Content Security Policy (CSP) headers.
- Use secure cookies with
HttpOnly
andSecure
flags.
-
- Limited support for non-React libraries.
- Requires a Node.js server for server-side rendering.
- Some features may not be compatible with static site generation.
- Learning curve for developers new to React or Next.js.
-
Yes, Next.js is suitable for large-scale applications due to its features like server-side rendering, static site generation, and API routes. It also supports code splitting, dynamic imports, and incremental static regeneration, which help in managing large codebases efficiently.
-
Next.js is considered a full-stack framework because it allows developers to build both the frontend and backend of web applications within a single codebase. It provides features like server-side rendering, static site generation, API routes, and database integration, enabling the development of complete web applications without needing separate frameworks for the frontend and backend.
-
To prevent API routes from being accessed by the client, you can implement authentication and authorization checks in your API route handlers. This ensures that only authenticated users can access the API endpoints.
export default function handler(req, res) { const token = req.headers.authorization; if (!token || !isValidToken(token)) { return res.status(401).json({ error: "Unauthorized" }); } // Handle the request res.status(200).json({ message: "Success" }); }
-
JSON Web Tokens (JWT) can be used in Next.js for authentication and authorization. You can create a JWT token upon user login and store it in a cookie or local storage. Then, you can verify the token in API routes or server-side functions to authenticate users.
import jwt from "jsonwebtoken"; // Create a JWT token const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: "1h", }); // Verify the JWT token jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => { if (err) { return res.status(401).json({ error: "Invalid token" }); } // Proceed with authenticated user });
-
By creating a
globals.css
file in theapp
directory and importing it in the_app.js
file./* app/globals.css */ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }
Then import it in your
_app.js
:// app/_app.js import "../globals.css"; export default function MyApp({ Component, pageProps }) { return <Component {...pageProps} />; }
-
The Pages Router is a file-based routing system in Next.js that automatically creates routes based on the file structure inside the
pages
directory. -
In Next.js pages router, you create routes by adding files to the
pages
directory:pages/index.js
- the homepage (/)pages/about.js
- the about page (/about)pages/blog/index.js
- the blog index page (/blog)pages/blog/[slug].js
- dynamic blog posts (/blog/:slug)
-
In the pages directory, you can add bracket syntax to create dynamic routes:
pages/posts/[id].js → /posts/1, /posts/2, etc. pages/[username]/settings.js → /foo/settings, /bar/settings, etc. pages/post/[...all].js → /post/2020/id/title, etc.
-
A catch-all segment allows you to match multiple segments in a dynamic route. It is defined using
[[...param]]
syntax.This allows you to create routes that can match multiple segments, such as
/docs/nextjs
,/docs/react
, etc.// pages/docs/[[...slug]].js export default function Docs({ params }) { return <div>Docs: {params.slug.join("/")}</div>; }
-
A special file for initializing pages. It's used for layout, state, or custom error handling.
-
A custom document for augmenting the application's HTML and body tags.
-
_app.js is for page initialization, _document.js is for custom document structure.
-
The
_error.js
file is used to create a custom error page for handling errors such as 404 and 500 in Next.js applications. -
By adding a
pages/404.js
file. -
Using getStaticProps or getServerSideProps in server side.
// getStaticProps export async function getStaticProps() { const res = await fetch("https://api.github.com/repos/vercel/next.js"); const repo = await res.json(); return { props: { repo } }; } export default function Page({ repo }) { return repo.stargazers_count; }
// getServerSideProps export async function getServerSideProps() { // Fetch data from external API const res = await fetch("https://api.github.com/repos/vercel/next.js"); const repo = await res.json(); // Pass data to the page via props return { props: { repo } }; } export default function Page({ repo }) { return ( <main> <p>{repo.stargazers_count}</p> </main> ); }
-
A function that runs at build time to fetch data for a page.
export async function getStaticProps(context) { const res = await fetch(`https://...`); const data = await res.json(); if (!data) { return { notFound: true, }; } return { props: { data }, // will be passed to the page component as props }; }
-
A function that runs on each request to fetch data for a page.
export async function getServerSideProps(context) { const res = await fetch(`https://...`); const data = await res.json(); if (!data) { return { notFound: true, }; } return { props: { data }, // will be passed to the page component as props }; }
-
getStaticProps runs at build time, getServerSideProps runs on each request.
-
A function that specifies dynamic routes to pre-render based on data.
export async function getStaticPaths() { const res = await fetch("https://.../posts"); const posts = await res.json(); // Get the paths we want to pre-render based on posts const paths = posts.map((post) => ({ params: { id: post.id }, })); // We'll pre-render only these paths at build time. // { fallback: false } means other routes should 404. return { paths, fallback: false }; }
-
Determines how to handle missing paths, with true, false, or 'blocking'.
export async function getStaticPaths() { const paths = await getAllPostIds(); return { paths, fallback: true, // this will enable fallback for all paths which are not generated at build time }; }
export async function getStaticPaths() { const paths = await getAllPostIds(); return { paths, fallback: false, // this will return 404 for all paths which are not generated at build time }; }
export async function getStaticPaths() { const paths = await getAllPostIds(); return { paths, fallback: "blocking", // this will return a static page for all paths which are not generated at build time }; }
-
By creating files in the
pages/api
directory, which will be treated as API endpoints.// pages/api/hello.js export default function handler(req, res) { res.status(200).json({ name: "John Doe" }); }
You can access this API route at
/api/hello
. -
By creating a
_error.js
file in thepages
directory.// pages/_error.js export default function Error({ statusCode }) { return ( <p> {statusCode ? `An error ${statusCode} occurred on server` : "An error occurred on client"} </p> ); }
-
Yes, the Pages Router has some limitations compared to the App Router, such as:
- Limited support for nested routes and layouts.
- Less flexibility in handling server components.
- No support for React Server Components.
-
By using libraries like
next-auth
or implementing custom authentication logic in API routes.// pages/api/auth/[...nextauth].js import NextAuth from "next-auth"; import Providers from "next-auth/providers"; export default NextAuth({ providers: [ Providers.Google({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, }), ], // Add more configuration options as needed });
-
By creating a custom server or using API routes to implement middleware logic.
// pages/api/middleware.js export default function middleware(req, res, next) { // Custom middleware logic if (req.headers.authorization) { next(); // Proceed to the next handler } else { res.status(401).json({ error: "Unauthorized" }); } }
-
By using client-side form handling or API routes for server-side handling.
// pages/contact.js export default function Contact() { const handleSubmit = async (e) => { e.preventDefault(); const formData = new FormData(e.target); const response = await fetch("/api/contact", { method: "POST", body: formData, }); if (response.ok) { alert("Form submitted successfully!"); } else { alert("Error submitting form."); } }; return ( <form onSubmit={handleSubmit}> <input name="name" type="text" placeholder="Name" required /> <input name="email" type="email" placeholder="Email" required /> <button type="submit">Submit</button> </form> ); }
-
Yes, you can use features like static generation (SSG), server-side rendering (SSR), and incremental static regeneration (ISR) to optimize performance in the Pages Router.
- Static Generation (SSG): Pre-render pages at build time.
- Server-Side Rendering (SSR): Render pages on each request.
- Incremental Static Regeneration (ISR): Update static pages after the build.
-
By using the
next-i18next
library or the built-in internationalization features in Next.js.// next.config.js module.exports = { i18n: { locales: ["en", "fr"], defaultLocale: "en", }, };
Then, you can use the
useTranslation
hook fromnext-i18next
to handle translations in your components.import { useTranslation } from "next-i18next"; export default function Home() { const { t } = useTranslation("common"); return <h1>{t("welcome")}</h1>; }
-
By using the
next/head
component to manage meta tags and other SEO-related elements.import Head from "next/head"; export default function Home() { return ( <> <Head> <title>My Next.js App</title> <meta name="description" content="A description of my Next.js app" /> </Head> <h1>Welcome to My Next.js App</h1> </> ); }
-
By placing static assets in the
public
directory, which is served at the root URL.public/ ├── images/ │ └── logo.png └── favicon.ico
You can access these files using
/images/logo.png
or/favicon.ico
. -
Next.js uses a built-in caching mechanism for static assets and API routes. You can also implement custom caching strategies using HTTP headers or libraries like
next-cache
.- Static Assets: Cached by default with a long cache lifetime.
- API Routes: Can be cached using HTTP headers like
Cache-Control
.
export default function handler(req, res) { res.setHeader("Cache-Control", "public, max-age=3600, immutable"); res.status(200).json({ message: "Cached response" }); }
-
Cache revalidation can be handled using the
revalidate
option ingetStaticProps
or by setting appropriate HTTP headers in API routes.- Using
revalidate
: Automatically revalidates static pages after a specified time.
export async function getStaticProps() { const res = await fetch("https://api.example.com/data"); const data = await res.json(); return { props: { data }, revalidate: 10, // Revalidate every 10 seconds }; }
- Using HTTP Headers: Set cache control headers in API routes.
export default function handler(req, res) { res.setHeader("Cache-Control", "s-maxage=10, stale-while-revalidate"); res.status(200).json({ message: "Revalidated response" }); }
- Using
-
By using the
next/image
component, which automatically optimizes images for performance.import Image from "next/image"; export default function Home() { return ( <div> <h1>My Next.js App</h1> <Image src="/images/logo.png" alt="Logo" width={500} height={300} quality={75} /> </div> ); }
The
next/image
component provides features like lazy loading, responsive images, and automatic format selection. -
You might choose the Pages Router over the App Router in the following scenarios:
- When you need a simple file-based routing system without complex nested routes.
- When you prefer the traditional Next.js routing approach.
- When your application does not require advanced features like server components or layouts.
The Pages Router is suitable for smaller applications or when you want to leverage existing knowledge of Next.js routing.
-
You might choose the App Router over the Pages Router in the following scenarios:
- When you need advanced routing capabilities, such as nested routes and layouts.
- When you want to leverage React Server Components for better performance and flexibility.
- When your application requires more complex data fetching strategies.
The App Router is suitable for larger applications or when you want to take advantage of the latest features in Next.js 13.
-
The App Router is a new routing system introduced in Next.js 13 that allows for more flexible and powerful routing capabilities, including nested routes, layouts, and server components.
-
In the App Router, you create routes by adding files to the
app
directory. Each file corresponds to a route, and you can create nested routes by creating subdirectories.app/ ├── page.js // Home page ├── about/ │ └── page.js // About page └── blog/ ├── page.js // Blog index page └── [slug]/ └── page.js // Dynamic blog post page
-
In the App Router, you create dynamic routes by using square brackets in the file name. For example, to create a dynamic blog post route, you would create a file named
[slug]/page.js
inside theblog
directory.app/ └── blog/ └── [slug]/ └── page.js // Dynamic blog post page
-
By creating error.js files in app directory.
// app/error.js export default function ErrorPage() { return <h1>Custom Error Page</h1>; }
-
Using client-side form handling or API routes for server-side handling.
-
By creating a middleware.js file in the app directory.
-
Using next-auth or a custom authentication solution.
-
To add authjs in nextjs app router, you can use the
next-auth
package. First, install it:npm install next-auth
Then, create a file named
[...nextauth].js
in theapp/api/auth
directory and configure your authentication providers.import NextAuth from "next-auth"; import Providers from "next-auth/providers"; export default NextAuth({ providers: [ Providers.Google({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, }), ], // Add more configuration options as needed callbacks: { async session(session, user) { // Add custom session properties here return session; }, }, pages: { signIn: "/auth/signin", // Custom sign-in page error: "/auth/error", // Error page }, secret: process.env.NEXTAUTH_SECRET, // Required for JWT encryption session: { jwt: true, // Use JWT for session management }, jwt: { secret: process.env.NEXTAUTH_JWT_SECRET, // Required for JWT encryption }, events: { signIn: async (message) => { // Custom logic after sign-in console.log("User signed in:", message); }, }, debug: process.env.NODE_ENV === "development", // Enable debug mode in development theme: { colorScheme: "light", // Change to "dark" for dark mode brandColor: "#0000FF", // Custom brand color logo: "/logo.png", // Custom logo URL }, pages: { signIn: "/auth/signin", // Custom sign-in page signOut: "/auth/signout", // Custom sign-out page error: "/auth/error", // Error page verifyRequest: "/auth/verify-request", // Verification request page newUser: null, // Will disable the new account creation screen }, });
-
By using cookies or local storage to store authentication tokens.
// Example of setting a token in a cookie import Cookies from "js-cookie"; function setToken(token) { Cookies.set("authToken", token, { expires: 7 }); // Expires in 7 days } function getToken() { return Cookies.get("authToken"); }
-
To add credentials in Next.js app router, you can use the
next-auth
package with the Credentials provider. First, install it:npm install next-auth
Then, create a file named
[...nextauth].js
in theapp/api/auth
directory and configure your credentials provider.import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; export default NextAuth({ providers: [ CredentialsProvider({ name: "Credentials", credentials: { username: { label: "Username", type: "text" }, password: { label: "Password", type: "password" }, }, async authorize(credentials) { // Add your own logic to validate credentials here const user = { id: 1, name: "John Doe" }; // Example user if ( credentials.username === "admin" && credentials.password === "password" ) { return user; // Return user object if credentials are valid } else { return null; // Return null if credentials are invalid } }, }), ], pages: { signIn: "/auth/signin", // Custom sign-in page }, callbacks: { async session(session, user) { session.user = user; // Add user object to session return session; }, }, secret: process.env.NEXTAUTH_SECRET, // Required for JWT encryption session: { jwt: true, // Use JWT for session management }, jwt: { secret: process.env.NEXTAUTH_JWT_SECRET, // Required for JWT encryption }, });
-
The
use server
directive is used to indicate that a function should be executed on the server side. It allows you to write server-side logic in a component or function that can be called from the client side."use server"; export async function myServerFunction() { // Server-side logic here return "Hello from the server!"; }
-
Using
use server
: The function is executed on the server side, allowing access to server-side resources and APIs. It can be used to perform operations that require server-side logic, such as database queries or API calls.Not using
use server
: The function is executed on the client side, meaning it cannot access server-side resources directly. It can only perform operations that are available in the client environment, such as manipulating the DOM or making client-side API calls.-
Example with
use server
:"use server"; export async function fetchData() { const response = await fetch("https://api.example.com/data"); const data = await response.json(); return data; }
-
Example without
use server
:export async function fetchData() { const response = await fetch("https://api.example.com/data"); const data = await response.json(); return data; }
-
Example with
use server
In Component:import { fetchData } from "./path/to/your/file"; export default function MyComponent() { const data = await fetchData(); // This will run on the server side return <div>{data}</div>; }
-
Example without
use server
In Component:import { fetchData } from "./path/to/your/file"; export default function MyComponent() { const data = fetchData(); // This will run on the client side return <div>{data}</div>; }
-
-
By creating files in the
app/api
directory, where each file corresponds to an API endpoint.// app/api/hello/route.js export async function GET(request) { return new Response("Hello, World!"); }
-
Middleware in Next.js allows you to run code before a request is completed. You can use it to modify the request or response, redirect users, or perform authentication checks.
// app/middleware.js import { NextResponse } from "next/server"; export function middleware(request) { // Perform some logic here if (request.nextUrl.pathname === "/") { return NextResponse.redirect(new URL("/home", request.url)); } return NextResponse.next(); }
You can also specify which paths the middleware should apply to:
// app/middleware.js export const config = { matcher: ["/about/:path*", "/blog/:path*"], };
-
The
formAction
is a special attribute used in Next.js to define the action URL for a form submission. It allows you to specify a server-side function that will handle the form submission.// app/form-example/page.js "use server"; export async function handleSubmit(formData) { const name = formData.get("name"); console.log("Form submitted with name:", name); return { success: true }; } export default function FormExample() { return ( <form action={handleSubmit}> <input type="text" name="name" /> <button type="submit">Submit</button> </form> ); }
In this example, when the form is submitted, the
handleSubmit
function will be called on the server side with the form data. -
By using the
formData
API in a server action to handle file uploads.// app/upload/page.js "use server"; export async function handleUpload(formData) { const file = formData.get("file"); // Process the file (e.g., save it to a storage service) console.log("File uploaded:", file.name); return { success: true }; } export default function UploadPage() { return ( <form action={handleUpload} encType="multipart/form-data"> <input type="file" name="file" /> <button type="submit">Upload</button> </form> ); }
-
- Creating nested routes with layouts.
- Implementing server-side rendering for dynamic content.
- Handling API routes for backend functionality.
- Managing authentication and authorization flows.
- Building complex applications with shared layouts and components.
-
The App Router allows for nested routes, layouts, and server components, while the Pages Router uses a flat file structure for routing and does not support nested routes or layouts.
-
The
use client
directive is used to indicate that a component should be rendered on the client side. It allows you to write client-side logic in a component that can be executed in the browser."use client"; export default function ClientComponent() { return <div>This component is rendered on the client side.</div>; }
-
Yes, it is possible to use both App Router and Pages Router in the same Next.js application. You can have the
app
directory for the App Router and thepages
directory for the Pages Router, allowing you to take advantage of both routing systems. -
Yes, some limitations of the App Router include:
- It is only available in Next.js 13 and later versions.
- It may not support all features available in the Pages Router.
- Some third-party libraries may not be compatible with the App Router.
- Many features from the Pages Router, such as
getStaticProps
andgetServerSideProps
, are not available in the App Router. - The App Router is still evolving, and some features may change or be added in future releases.
-
Authorization in middleware and routes in Next.js involves checking if a user has the necessary permissions to access a specific route or perform an action. This can be done by verifying user roles, permissions, or tokens in the middleware function before allowing access to the route.
// app/middleware.js import { NextResponse } from "next/server"; export function middleware(request) { const token = request.cookies.get("authToken"); if (!token) { return NextResponse.redirect(new URL("/login", request.url)); } // Additional authorization logic can go here return NextResponse.next(); } export const config = { matcher: ["/protected/:path*"], // Apply middleware to protected routes };
In this example, the middleware checks for an authentication token in the cookies. If the token is not present, it redirects the user to the login page. If the token is valid, it allows access to the protected routes.
// app/api/protected/route.js import { NextResponse } from "next/server"; export async function GET(request) { const token = request.cookies.get("authToken"); if (!token) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } // Handle the request for authorized users return NextResponse.json({ message: "Protected data" }); }
In this example, the API route checks for the authentication token in the request cookies. If the token is not present, it returns a 401 Unauthorized response. If the token is valid, it returns the protected data.
-
-
use server
: Indicates that the function should be executed on the server side. It allows you to write server-side logic that can be called from the client side. -
use client
: Indicates that the component should be rendered on the client side. It allows you to write client-side logic that can be executed in the browser.
// Example of use server "use server"; export async function fetchData() { const response = await fetch("https://api.example.com/data"); return response.json(); } // Example of use client ("use client"); export default function ClientComponent() { return <div>This component is rendered on the client side.</div>; }
-
-
Server actions in Next.js allow you to define functions that can be executed on the server side when a form is submitted or an action is triggered. These functions can handle data processing, database interactions, or any server-side logic.
// app/actions/submitForm.js "use server"; export async function submitForm(formData) { const name = formData.get("name"); console.log("Form submitted with name:", name); return { success: true }; }
You can then use this action in a form:
// app/form/page.js import { submitForm } from "../actions/submitForm"; export default function FormPage() { return ( <form action={submitForm}> <input type="text" name="name" /> <button type="submit">Submit</button> </form> ); }
-
- Performance: Server actions allow you to offload heavy computations or data processing to the server, reducing the load on the client.
- Security: Sensitive operations can be performed on the server, preventing exposure of sensitive data or logic to the client.
- Simplified Data Fetching: You can fetch data directly in server actions without needing to manage client-side state or effects.
- Reduced Client Bundle Size: By moving logic to the server, you can reduce the amount of JavaScript sent to the client, improving load times.
-
- Latency: Server actions can introduce latency since they require a round trip to the server, which may not be ideal for real-time interactions.
- Complexity: Managing server actions can add complexity to your application, especially if you have many actions or need to handle different states.
- Limited Client-Side Interactivity: Since server actions are executed on the server, they may not provide the same level of interactivity as client-side functions.
- Debugging Challenges: Debugging server actions can be more challenging compared to client-side code, as you may not have access to browser developer tools.
-
- API Routes: You can create API routes to handle server-side logic and data fetching, which can be called from the client side.
- Client-Side Fetching: Use client-side data fetching methods like
useEffect
or libraries like SWR or React Query to manage data on the client side. - Static Site Generation (SSG): Use SSG for pages that can be pre-rendered at build time, reducing the need for server actions.
- Server-Side Rendering (SSR): Use SSR for dynamic pages that require server-side data fetching on each request.
-
Instead of using server actions, you can use API routes to handle form submissions or data processing. Here's an example:
// app/api/submitForm/route.js export async function POST(request) { const formData = await request.formData(); const name = formData.get("name"); console.log("Form submitted with name:", name); return new Response(JSON.stringify({ success: true }), { status: 200, headers: { "Content-Type": "application/json" }, }); }
You can then call this API route from a client-side component:
// app/form/page.js export default function FormPage() { const handleSubmit = async (event) => { event.preventDefault(); const formData = new FormData(event.target); const response = await fetch("/api/submitForm", { method: "POST", body: formData, }); const result = await response.json(); console.log(result); }; return ( <form onSubmit={handleSubmit}> <input type="text" name="name" /> <button type="submit">Submit</button> </form> ); }
-
JSON Web Tokens (JWT) can be used in the Next.js App Router for authentication and authorization. You can create a JWT token upon user login and store it in a cookie or local storage. Then, you can verify the token in API routes or server-side functions to authenticate users.
import jwt from "jsonwebtoken"; // Create a JWT token const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: "1h", }); // Verify the JWT token jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => { if (err) { return res.status(401).json({ error: "Invalid token" }); } // Proceed with authenticated user });
Using it on application:
// app/api/auth/route.js import { NextResponse } from "next/server"; import jwt from "jsonwebtoken"; export async function POST(request) { const { token } = await request.json(); try { const decoded = jwt.verify(token, process.env.JWT_SECRET); return NextResponse.json({ userId: decoded.userId }); } catch (error) { return NextResponse.json({ error: "Invalid token" }, { status: 401 }); } }
You can also use JWT tokens for protecting API routes:
// app/api/protected/route.js import { NextResponse } from "next/server"; import jwt from "jsonwebtoken"; export async function GET(request) { const token = request.cookies.get("authToken"); if (!token) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } try { const decoded = jwt.verify(token, process.env.JWT_SECRET); // Handle the request for authorized users return NextResponse.json({ message: "Protected data", userId: decoded.userId, }); } catch (error) { return NextResponse.json({ error: "Invalid token" }, { status: 401 }); } }
-
The context of using JWT tokens in the Next.js App Router is primarily for authentication and authorization purposes. JWT tokens allow you to securely transmit user information between the client and server, enabling you to verify user identity and permissions without needing to store session data on the server.
This approach is particularly useful for stateless applications where you want to maintain user sessions without relying on server-side session storage.
-
The App Router offers more flexibility and features compared to the Pages Router, such as nested routes, layouts, and server components. It is designed for building complex applications with shared layouts and components.
However, the choice between App Router and Pages Router depends on your specific use case. If you need simple routing without nested routes or layouts, the Pages Router may be sufficient.
-
You can handle global state management in Next.js with the App Router using libraries like Redux, Zustand, or React Context API. These libraries allow you to create a global store that can be accessed from any component in your application.
- Using React Context API:
// app/context/GlobalState.js import { createContext, useContext, useState } from "react"; const GlobalStateContext = createContext(); export function GlobalStateProvider({ children }) { const [state, setState] = useState({ user: null }); return ( <GlobalStateContext.Provider value={{ state, setState }}> {children} </GlobalStateContext.Provider> ); } export function useGlobalState() { return useContext(GlobalStateContext); }
Then wrap your application with the
GlobalStateProvider
:// app/layout.js import { GlobalStateProvider } from "./context/GlobalState"; export default function RootLayout({ children }) { return ( <html lang="en"> <body> <GlobalStateProvider>{children}</GlobalStateProvider> </body> </html> ); }
Now you can access the global state in any component using the
useGlobalState
hook.// app/page.js import { useGlobalState } from "./context/GlobalState"; export default function HomePage() { const { state, setState } = useGlobalState(); return ( <div> <h1>Welcome, {state.user ? state.user.name : "Guest"}</h1> <button onClick={() => setState({ user: { name: "John Doe" } })}> Log In </button> </div> ); }
-
In the Next.js App Router, you can use the
fetch
API to make HTTP requests to external APIs or your own API routes. Thefetch
function is available globally in both server and client components.- Example of using fetch in a server component:
// app/api/data/route.js export async function GET() { const response = await fetch("https://api.example.com/data"); const data = await response.json(); return new Response(JSON.stringify(data), { headers: { "Content-Type": "application/json" }, }); }
- Example of using fetch in a client component:
// app/page.js import { useEffect, useState } from "react"; export default function HomePage() { const [data, setData] = useState(null); useEffect(() => { async function fetchData() { const response = await fetch("/api/data"); const result = await response.json(); setData(result); } fetchData(); }, []); return ( <div> <h1>Data from API</h1> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); }