gavmac00/nextjs-app-docs huggingface dataset:
usage of <Head> in pages/_document.js.@next/next/no-head-elementPrevent usage of <head> element.@next/next/no-head-import-in-documentPrevent usage of next/head in pages/_document.js.@next/next/no-html-link-for-pagesPrevent usage of <a> elements to navigate to internal Next.js pages.@next/next/no-img-elementPrevent usage of <img> element due to slower LCP and higher bandwidth.@next/next/no-page-custom-fontPrevent page-only custom fonts.@next/next/no-script-component-in-headPrevent usage of next/script in next/head component.@next/next/no-styled-jsx-in-documentPrevent usage of styled-jsx in pages/_document.js.@next/next/no-sync-scriptsPrevent synchronous scripts.@next/next/no-title-in-document-headPrevent usage of <title> with Head component from next/document.@next/next/no-typosPrevent common typos in Next.js's data fetching
common typos in Next.js's data fetching functions@next/next/no-unwanted-polyfillioPrevent duplicate polyfills from Polyfill.io.
If you already have ESLint configured in your application, we recommend extending from this plugin directly instead of including eslint-config-next unless a few conditions are met. Refer to the Recommended Plugin Ruleset to learn more.
Custom Settings
rootDir
If you're using eslint-plugin-next in a project where Next.js isn't installed in your root directory (such as a monorepo), you can tell eslint-plugin-next where to find your Next.js application using the settings property in your .eslintrc:
.eslintrc.json {
  "extends": "next",
  "settings": {
    "next": {
      "rootDir": "packages/my-app/"
    }
  }
}
rootDir can be a path (relative or absolute), a glob (i.e. "packages/*/"), or an array of paths and/or globs.
Linting Custom Directories and Files
Linting Custom Directories and Files
By default, Next.js will run ESLint for all files in the pages/, app/, components/, lib/, and src/ directories. However, you can specify which directories using the dirs option in the eslint config in next.config.js for production builds:
next.config.js module.exports = {
  eslint: {
    dirs: ['pages', 'utils'], // Only run ESLint on the 'pages' and 'utils' directories during production builds (next build)
  },
}
Similarly, the --dir and --file flags can be used for next lint to lint specific directories and files:
Terminal next lint --dir pages --dir utils --file bar.js
Caching
Terminal next lint --dir pages --dir utils --file bar.js
Caching
To improve performance, information of files processed by ESLint are cached by default. This is stored in .next/cache or in your defined build directory. If you include any ESLint rules that depend on more than the contents of a single source file and need to disable the cache, use the --no-cache flag with next lint.
Terminal next lint --no-cache
Disabling Rules
If you would like to modify or disable any rules provided by the supported plugins (react, react-hooks, next), you can directly change them using the rules property in your .eslintrc:
.eslintrc.json {
  "extends": "next",
  "rules": {
    "react/no-unescaped-entities": "off",
    "@next/next/no-page-custom-font": "off"
  }
}
Core Web Vitals
}
}
Core Web Vitals
The next/core-web-vitals rule set is enabled when next lint is run for the first time and the strict option is selected.
.eslintrc.json {
  "extends": "next/core-web-vitals"
}
next/core-web-vitals updates eslint-plugin-next to error on a number of rules that are warnings by default if they affect Core Web Vitals.
The next/core-web-vitals entry point is automatically included for new applications built with Create Next App.
Usage With Other Tools
Prettier
ESLint also contains code formatting rules, which can conflict with your existing Prettier setup. We recommend including eslint-config-prettier in your ESLint config to make ESLint and Prettier work together.
First, install the dependency:
Terminal npm install --save-dev eslint-config-prettier
 
yarn add --dev eslint-config-prettier
yarn add --dev eslint-config-prettier
Then, add prettier to your existing ESLint config:
.eslintrc.json {
  "extends": ["next", "prettier"]
}
lint-staged
If you would like to use next lint with lint-staged to run the linter on staged git files, you'll have to add the following to the .lintstagedrc.js file in the root of your project in order to specify usage of the --file flag.
.lintstagedrc.js const path = require('path')
 
const buildEslintCommand = (filenames) =>
  `next lint --fix --file ${filenames
    .map((f) => path.relative(process.cwd(), f))
    .join(' --file ')}`
 
module.exports = {
  '*.{js,jsx,ts,tsx}': [buildEslintCommand],
'*.{js,jsx,ts,tsx}': [buildEslintCommand],
}
Migrating Existing Config
Recommended Plugin Ruleset
If you already have ESLint configured in your application and any of the following conditions are true:
You have one or more of the following plugins already installed (either separately or through a different config such as airbnb or react-app):
react
react-hooks
jsx-a11y
import
You've defined specific parserOptions that are different from how Babel is configured within Next.js (this is not recommended unless you have customized your Babel configuration)
You have eslint-plugin-import installed with Node.js and/or TypeScript resolvers defined to handle imports
Then we recommend either removing these settings if you prefer how these properties have been configured within eslint-config-next or extending directly from the Next.js ESLint plugin instead:
 module.exports = {
  extends: [
    //...
module.exports = {
  extends: [
    //...
    'plugin:@next/next/recommended',
  ],
}
The plugin can be installed normally in your project without needing to run next lint:
Terminal npm install --save-dev @next/eslint-plugin-next
 
yarn add --dev @next/eslint-plugin-next
This eliminates the risk of collisions or errors that can occur due to importing the same plugin or parser across multiple configurations.
Additional Configurations
If you already use a separate ESLint configuration and want to include eslint-config-next, ensure that it is extended last after other configurations. For example:
.eslintrc.json {
  "extends": ["eslint:recommended", "next"]
}
The next configuration already handles setting default values for the parser, plugins and settings properties. There is no need to manually re-declare any of these properties unless you need a different configuration for your use case.
If you include any other shareable configurations, you will need to make sure that these properties are not overwritten or modified. Otherwise, we recommend removing any configurations that share behavior with the next configuration or extending directly from the Next.js ESLint plugin as mentioned above.
src Directory
As an alternative to having the special Next.js app or pages directories in the root of your project, Next.js also supports the common pattern of placing application code under the src directory.
This separates application code from project configuration files which mostly live in the root of a project. Which is preferred by some individuals and teams.
To use the src directory, move the app Router folder or pages Router folder to src/app or src/pages respectively.
Good to know
The /public directory should remain in the root of your project.
Config files like package.json, next.config.js and tsconfig.json should remain in the root of your project.
.env.* files should remain in the root of your project.
src/app or src/pages will be ignored if app or pages are present in the root directory.
If you're using src, you'll probably also move other application folders such as /components or /lib.
If you're using Tailwind CSS, you'll need to add the /src prefix to the tailwind.config.js file in the content section.
Absolute Imports and Module Path Aliases
Examples
Absolute Imports and Aliases
Next.js has in-built support for the "paths" and "baseUrl" options of tsconfig.json and jsconfig.json files.
These options allow you to alias project directories to absolute paths, making it easier to import modules. For example:
 // before
import { Button } from '../../../components/button'
 
// after
import { Button } from '@/components/button'
Good to know: create-next-app will prompt to configure these options for you.
Absolute Imports
The baseUrl configuration option allows you to import directly from the root of the project.
An example of this configuration:
tsconfig.json or jsconfig.json {
  "compilerOptions": {
    "baseUrl": "."
  }
}
components/button.tsx export default function Button() {
  return <button>Click me</button>
}
app/page.tsx import Button from 'components/button'
}
app/page.tsx import Button from 'components/button'
 
export default function HomePage() {
  return (
    <>
      <h1>Hello World</h1>
      <Button />
    </>
  )
}
Module Aliases
In addition to configuring the baseUrl path, you can use the "paths" option to "alias" module paths.
For example, the following configuration maps @/components/* to components/*:
tsconfig.json or jsconfig.json {
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/components/*": ["components/*"]
    }
  }
}
components/button.tsx export default function Button() {
  return <button>Click me</button>
}
app/page.tsx import Button from '@/components/button'
 
export default function HomePage() {
  return (
    <>
export default function HomePage() {
  return (
    <>
      <h1>Hello World</h1>
      <Button />
    </>
  )
}
Each of the "paths" are relative to the baseUrl location. For example:
 // tsconfig.json or jsconfig.json
{
  "compilerOptions": {
    "baseUrl": "src/",
    "paths": {
      "@/styles/*": ["styles/*"],
      "@/components/*": ["components/*"]
    }
  }
}
 // pages/index.js
import Button from '@/components/button'
import '@/styles/styles.css'
import Helper from 'utils/helper'
 
export default function HomePage() {
  return (
    <Helper>
      <h1>Hello World</h1>
      <Button />
    </Helper>
  )
}
MDX
Markdown is a lightweight markup language used to format text. It allows you to write using plain text syntax and convert it to structurally valid HTML. It's commonly used for writing content on websites and blogs.
You write...
 I **love** using [Next.js](https://nextjs.org/)
Output:
 <p>I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a></p>
MDX is a superset of markdown that lets you write JSX directly in your markdown files. It is a powerful way to add dynamic interactivity and embed React components within your content.
Next.js can support both local MDX content inside your application, as well as remote MDX files fetched dynamically on the server. The Next.js plugin handles transforming Markdown and React components into HTML, including support for usage in Server Components (default in app).
@next/mdx
@next/mdx
The @next/mdx package is configured in the next.config.js file at your projects root. It sources data from local files, allowing you to create pages with a .mdx extension, directly in your /pages or /app directory.
Getting Started
Install the @next/mdx package:Terminal npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdxCreate mdx-components.tsx in the root of your application (the parent folder of app/ or src/):mdx-components.tsx import type { MDXComponents } from 'mdx/types'
 
// This file allows you to provide custom React components
// to be used in MDX files. You can import and use any
// React component you want, including components from
// other libraries.
 
// This file is required to use MDX in `app` directory.
// This file is required to use MDX in `app` directory.
export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    // Allows customizing built-in components, e.g. to add styling.
    // h1: ({ children }) => <h1 style={{ fontSize: "100px" }}>{children}</h1>,
    ...components,
  }
}Update next.config.js to use mdxRs:next.config.js /** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    mdxRs: true,
  },
}
 
const withMDX = require('@next/mdx')()
module.exports = withMDX(nextConfig)Add a new file with MDX content to your app directory:app/hello.mdx Hello, Next.js!
You can import and use React components in MDX files.Import the MDX file inside a page to display the content:app/page.tsx import HelloWorld from './hello.mdx'
 
export default function Page() {
  return <HelloWorld />
}
Remote MDX
If your Markdown or MDX files do not live inside your application, you can fetch them dynamically on the server. This is useful for fetching content from a CMS or other data source.
There are two popular community packages for fetching MDX content: next-mdx-remote and contentlayer. For example, the following example uses next-mdx-remote:
Good to know: Please proceed with caution. MDX compiles to JavaScript and is executed on the server. You should only fetch MDX content from a trusted source, otherwise this can lead to remote code execution (RCE).
app/page.tsx import { MDXRemote } from 'next-mdx-remote/rsc'
 
export default async function Home() {
  const res = await fetch('https://...')
  const markdown = await res.text()
  return <MDXRemote source={markdown} />
}
Layouts
To share a layout around MDX content, you can use the built-in layouts support with the App Router.
Remark and Rehype Plugins
You can optionally provide remark and rehype plugins to transform the MDX content. For example, you can use remark-gfm to support GitHub Flavored Markdown.
Since the remark and rehype ecosystem is ESM only, you'll need to use next.config.mjs as the configuration file.
next.config.mjs import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'
import createMDX from '@next/mdx'
 
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
  },
}
 
const withMDX = createMDX({
  options: {
    extension: /\.mdx?$/,
    remarkPlugins: [remarkGfm],
    rehypePlugins: [],
    // If you use `MDXProvider`, uncomment the following line.
    // providerImportSource: "@mdx-js/react",
  },
})
export default withMDX(nextConfig)
Frontmatter
Frontmatter is a YAML like key/value pairing that can be used to store data about a page. @next/mdx does not support frontmatter by default, though there are many solutions for adding frontmatter to your MDX content, such as gray-matter.
To access page metadata with @next/mdx, you can export a meta object from within the .mdx file:
 export const meta = {
  author: 'Rich Haines',
}
 
# My MDX page
Custom Elements
One of the pleasant aspects of using markdown, is that it maps to native HTML elements, making writing fast, and intuitive:
 This is a list in markdown:
 
- One
- Two
- Three
The above generates the following HTML:
 <p>This is a list in markdown:</p>
 
<ul>
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>
<li>Three</li>
</ul>
When you want to style your own elements to give a custom feel to your website or application, you can pass in shortcodes. These are your own custom components that map to HTML elements. To do this you use the MDXProvider and pass a components object as a prop. Each object key in the components object maps to a HTML element name.
To enable you need to specify providerImportSource: "@mdx-js/react" in next.config.js.
next.config.js const withMDX = require('@next/mdx')({
  // ...
  options: {
    providerImportSource: '@mdx-js/react',
  },
})
Then setup the provider in your page
pages/index.js import { MDXProvider } from '@mdx-js/react'
import Image from 'next/image'
import { Heading, InlineCode, Pre, Table, Text } from 'my-components'
import { Heading, InlineCode, Pre, Table, Text } from 'my-components'
 
const ResponsiveImage = (props) => (
  <Image
    alt={props.alt}
    sizes="100vw"
    style={{ width: '100%', height: 'auto' }}
    {...props}
  />
)
 
const components = {
  img: ResponsiveImage,
  h1: Heading.H1,
  h2: Heading.H2,
  p: Text,
  pre: Pre,
  code: InlineCode,
}
 
export default function Post(props) {
  return (
    <MDXProvider components={components}>
      <main {...props} />
    </MDXProvider>
  )
}
<main {...props} />
    </MDXProvider>
  )
}
If you use it across the site you may want to add the provider to _app.js so all MDX pages pick up the custom element config.
Deep Dive: How do you transform markdown into HTML?
React does not natively understand Markdown. The markdown plaintext needs to first be transformed into HTML. This can be accomplished with remark and rehype.
remark is an ecosystem of tools around markdown. rehype is the same, but for HTML. For example, the following code snippet transforms markdown into HTML:
 import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'
 
main()
 
async function main() {
main()
 
async function main() {
  const file = await unified()
    .use(remarkParse) // Convert into markdown AST
    .use(remarkRehype) // Transform to HTML AST
    .use(rehypeSanitize) // Sanitize HTML input
    .use(rehypeStringify) // Convert AST into serialized HTML
    .process('Hello, Next.js!')
 
  console.log(String(file)) // <p>Hello, Next.js!</p>
}
The remark and rehype ecosystem contains plugins for syntax highlighting, linking headings, generating a table of contents, and more.
When using @next/mdx as shown below, you do not need to use remark or rehype directly, as it is handled for you.
Using the Rust-based MDX compiler (Experimental)
Using the Rust-based MDX compiler (Experimental)
Next.js supports a new MDX compiler written in Rust. This compiler is still experimental and is not recommended for production use. To use the new compiler, you need to configure next.config.js when you pass it to withMDX:
next.config.js module.exports = withMDX({
  experimental: {
    mdxRs: true,
  },
})
Helpful Links
MDX
@next/mdx
remark
rehype
Draft ModeStatic rendering is useful when your pages fetch data from a headless CMS. However, it’s not ideal when you’re writing a draft on your headless CMS and want to view the draft immediately on your page. You’d want Next.js to render these pages at request time instead of build time and fetch the draft content instead of the published content. You’d want Next.js to switch to dynamic rendering only for this specific case.
Next.js has a feature called Draft Mode which solves this problem. Here are instructions on how to use it.
Step 1: Create and access the Route Handler
First, create a Route Handler. It can have any name - e.g. app/api/draft/route.ts
Then, import draftMode from next/headers and call the enable() method.
app/api/draft/route.ts // route handler enabling draft mode
import { draftMode } from 'next/headers'
import { draftMode } from 'next/headers'
 
export async function GET(request: Request) {
  draftMode().enable()
  return new Response('Draft mode is enabled')
}
This will set a cookie to enable draft mode. Subsequent requests containing this cookie will trigger Draft Mode changing the behavior for statically generated pages (more on this later).
You can test this manually by visiting /api/draft and looking at your browser’s developer tools. Notice the Set-Cookie response header with a cookie named __prerender_bypass.
Securely accessing it from your Headless CMS
In practice, you’d want to call this Route Handler securely from your headless CMS. The specific steps will vary depending on which headless CMS you’re using, but here are some common steps you could take.
These steps assume that the headless CMS you’re using supports setting custom draft URLs. If it doesn’t, you can still use this method to secure your draft URLs, but you’ll need to construct and access the draft URL manually.
First, you should create a secret token string using a token generator of your choice. This secret will only be known by your Next.js app and your headless CMS. This secret prevents people who don’t have access to your CMS from accessing draft URLs.
Second, if your headless CMS supports setting custom draft URLs, specify the following as the draft URL. This assumes that your Route Handler is located at app/api/draft/route.ts
Terminal https://<your-site>/api/draft?secret=<token>&slug=<path>
<your-site> should be your deployment domain.
<token> should be replaced with the secret token you generated.
<token> should be replaced with the secret token you generated.
<path> should be the path for the page that you want to view. If you want to view /posts/foo, then you should use &slug=/posts/foo.
Your headless CMS might allow you to include a variable in the draft URL so that <path> can be set dynamically based on the CMS’s data like so: &slug=/posts/{entry.fields.slug}
Finally, in the Route Handler:
Check that the secret matches and that the slug parameter exists (if not, the request should fail).
Call draftMode.enable() to set the cookie.
Then redirect the browser to the path specified by slug.
app/api/draft/route.ts // route handler with secret and slug
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
 
export async function GET(request: Request) {
  // Parse query string parameters
export async function GET(request: Request) {
  // Parse query string parameters
  const { searchParams } = new URL(request.url)
  const secret = searchParams.get('secret')
  const slug = searchParams.get('slug')
 
  // Check the secret and next parameters
  // This secret should only be known to this route handler and the CMS
  if (secret !== 'MY_SECRET_TOKEN' || !slug) {
    return new Response('Invalid token', { status: 401 })
  }
 
  // Fetch the headless CMS to check if the provided `slug` exists
  // getPostBySlug would implement the required fetching logic to the headless CMS
  const post = await getPostBySlug(slug)
 
  // If the slug doesn't exist prevent draft mode from being enabled
  if (!post) {
if (!post) {
    return new Response('Invalid slug', { status: 401 })
  }
 
  // Enable Draft Mode by setting the cookie
  draftMode().enable()
 
  // Redirect to the path from the fetched post
  // We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities
  redirect(post.slug)
}
If it succeeds, then the browser will be redirected to the path you want to view with the draft mode cookie.
Step 2: Update page
The next step is to update your page to check the value of draftMode().isEnabled.
If you request a page which has the cookie set, then data will be fetched at request time (instead of at build time).
Furthermore, the value of isEnabled will be true.
app/page.tsx // page that fetches data
import { draftMode } from 'next/headers'
import { draftMode } from 'next/headers'
 
async function getData() {
  const { isEnabled } = draftMode()
 
  const url = isEnabled
    ? 'https://draft.example.com'
    : 'https://production.example.com'
 
  const res = await fetch(url)
 
  return res.json()
}
 
export default async function Page() {
  const { title, desc } = await getData()
 
  return (
    <main>
      <h1>{title}</h1>
      <p>{desc}</p>
    </main>
  )
}
That's it! If you access the draft Route Handler (with secret and slug) from your headless CMS or manually, you should now be able to see the draft content. And if you update your draft without publishing, you should be able to view the draft.
Set this as the draft URL on your headless CMS or access manually, and you should be able to see the draft.
Terminal https://<your-site>/api/draft?secret=<token>&slug=<path>
More Details
Clear the Draft Mode cookie
By default, the Draft Mode session ends when the browser is closed.
To clear the Draft Mode cookie manually, create a Route Handler that calls draftMode().disable():
app/api/disable-draft/route.ts import { draftMode } from 'next/headers'
 
export async function GET(request: Request) {
  draftMode().disable()
  return new Response('Draft mode is disabled')
}
Then, send a request to /api/disable-draft to invoke the Route Handler. If calling this route using next/link, you must pass prefetch={false} to prevent accidentally deleting the cookie on prefetch.
Unique per next build
Unique per next build
A new bypass cookie value will be generated each time you run next build.
This ensures that the bypass cookie can’t be guessed.
Good to know: To test Draft Mode locally over HTTP, your browser will need to allow third-party cookies and local storage access.