App Router Incremental Adoption GuideThis guide will help you:
Update your Next.js application from version 12 to version 13
Upgrade features that work in both the pages and the app directories
Incrementally migrate your existing application from pages to app
Upgrading
Node.js Version
The minimum Node.js version is now v16.8. See the Node.js documentation for more information.
Next.js Version
To update to Next.js version 13, run the following command using your preferred package manager:
Terminal npm install next@latest react@latest react-dom@latest
ESLint Version
If you're using ESLint, you need to upgrade your ESLint version:
Terminal npm install -D eslint-config-next@latest


Terminal npm install -D eslint-config-next@latest
Good to know: You may need to restart the ESLint server in VS Code for the ESLint changes to take effect. Open the Command Palette (cmd+shift+p on Mac; ctrl+shift+p on Windows) and search for ESLint: Restart ESLint Server.
Next Steps
After you've updated, see the following sections for next steps:
Upgrade new features: A guide to help you upgrade to new features such as the improved Image and Link Components.
Migrate from the pages to app directory: A step-by-step guide to help you incrementally migrate from the pages to the app directory.
Upgrading New Features
Next.js 13 introduced the new App Router with new features and conventions. The new Router is available in the app directory and co-exists with the pages directory.


Upgrading to Next.js 13 does not require using the new App Router. You can continue using pages with new features that work in both directories, such as the updated Image component, Link component, Script component, and Font optimization.
<Image/> Component
Next.js 12 introduced new improvements to the Image Component with a temporary import: next/future/image. These improvements included less client-side JavaScript, easier ways to extend and style images, better accessibility, and native browser lazy loading.
In version 13, this new behavior is now the default for next/image.
There are two codemods to help you migrate to the new Image Component:
next-image-to-legacy-image codemod: Safely and automatically renames next/image imports to next/legacy/image. Existing components will maintain the same behavior.


next-image-experimental codemod: Dangerously adds inline styles and removes unused props. This will change the behavior of existing components to match the new defaults. To use this codemod, you need to run the next-image-to-legacy-image codemod first.
<Link> Component
The <Link> Component no longer requires manually adding an  tag as a child. This behavior was added as an experimental option in version 12.2 and is now the default. In Next.js 13, <Link> always renders  and allows you to forward props to the underlying tag.
For example:
 import Link from 'next/link'

// Next.js 12: `<a>` has to be nested otherwise it's excluded
<Link href="/about">
  About
</Link>

// Next.js 13: `<Link>` always renders `<a>` under the hood


// Next.js 13: `<Link>` always renders `<a>` under the hood
<Link href="/about">
  About
</Link>
To upgrade your links to Next.js 13, you can use the new-link codemod.
<Script> Component
The behavior of next/script has been updated to support both pages and app, but some changes need to be made to ensure a smooth migration:
Move any beforeInteractive scripts you previously included in _document.js to the root layout file (app/layout.tsx).
The experimental worker strategy does not yet work in app and scripts denoted with this strategy will either have to be removed or modified to use a different strategy (e.g. lazyOnload).
onLoad, onReady, and onError handlers will not work in Server Components so make sure to move them to a Client Component or remove them altogether.
Font Optimization


Font Optimization
Previously, Next.js helped you optimize fonts by inlining font CSS. Version 13 introduces the new next/font module which gives you the ability to customize your font loading experience while still ensuring great performance and privacy. next/font is supported in both the pages and app directories.
While inlining CSS still works in pages, it does not work in app. You should use next/font instead.
See the Font Optimization page to learn how to use next/font.
Migrating from pages to app
🎥 Watch: Learn how to incrementally adopt the App Router → YouTube (16 minutes).
Moving to the App Router may be the first time using React features that Next.js builds on top of such as Server Components, Suspense, and more. When combined with new Next.js features such as special files and layouts, migration means new concepts, mental models, and behavioral changes to learn.


We recommend reducing the combined complexity of these updates by breaking down your migration into smaller steps. The app directory is intentionally designed to work simultaneously with the pages directory to allow for incremental page-by-page migration.
The app directory supports nested routes and layouts. Learn more.
Use nested folders to define routes and a special page.js file to make a route segment publicly accessible. Learn more.
Special file conventions are used to create UI for each route segment. The most common special files are page.js and layout.js.
Use page.js to define UI unique to a route.
Use layout.js to define UI that is shared across multiple routes.
.js, .jsx, or .tsx file extensions can be used for special files.
You can colocate other files inside the app directory such as components, styles, tests, and more. Learn more.


Data fetching functions like getServerSideProps and getStaticProps have been replaced with a new API inside app. getStaticPaths has been replaced with generateStaticParams.
pages/_app.js and pages/_document.js have been replaced with a single app/layout.js root layout. Learn more.
pages/_error.js has been replaced with more granular error.js special files. Learn more.
pages/404.js has been replaced with the not-found.js file.
pages/api/* currently remain inside the pages directory.
Step 1: Creating the app directory
Update to the latest Next.js version (requires 13.4 or greater):
 npm install next@latest
Then, create a new app directory at the root of your project (or src/ directory).
Step 2: Creating a Root Layout
Create a new app/layout.tsx file inside the app directory. This is a root layout that will apply to all routes inside app.


app/layout.tsx export default function RootLayout({
  // Layouts must accept a children prop.
  // This will be populated with nested layouts or pages
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
The app directory must include a root layout.
The root layout must define <html>, and <body> tags since Next.js does not automatically create them
The root layout replaces the pages/_app.tsx and pages/_document.tsx files.
.js, .jsx, or .tsx extensions can be used for layout files.
To manage <head> HTML elements, you can use the built-in SEO support:
app/layout.tsx import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Home',


export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
}
Migrating _document.js and _app.js
If you have an existing _app or _document file, you can copy the contents (e.g. global styles) to the root layout (app/layout.tsx). Styles in app/layout.tsx will not apply to pages/*. You should keep _app/_document while migrating to prevent your pages/* routes from breaking. Once fully migrated, you can then safely delete them.
If you are using any React Context providers, they will need to be moved to a Client Component.
Migrating the getLayout() pattern to Layouts (Optional)
Next.js recommended adding a property to Page components to achieve per-page layouts in the pages directory. This pattern can be replaced with native support for nested layouts in the app directory.


See before and after exampleBeforecomponents/DashboardLayout.js export default function DashboardLayout({ children }) {
  return (

My Dashboard

      {children}

  )
}pages/dashboard/index.js import DashboardLayout from '../components/DashboardLayout'

export default function Page() {
  return
My Page

}

Page.getLayout = function getLayout(page) {
  return <DashboardLayout>{page}</DashboardLayout>
}After
Remove the Page.getLayout property from pages/dashboard/index.js and follow the steps for migrating pages to the app directory.
app/dashboard/page.js export default function Page() {
  return
My Page

}
Move the contents of DashboardLayout into a new Client Component to retain pages directory behavior.


}
Move the contents of DashboardLayout into a new Client Component to retain pages directory behavior.
app/dashboard/DashboardLayout.js 'use client' // this directive should be at top of the file, before any imports.

// This is a Client Component
export default function DashboardLayout({ children }) {
  return (

My Dashboard

      {children}

  )
}
Import the DashboardLayout into a new layout.js file inside the app directory.
app/dashboard/layout.js import DashboardLayout from './DashboardLayout'

// This is a Server Component
export default function Layout({ children }) {
  return <DashboardLayout>{children}</DashboardLayout>
}
You can incrementally move non-interactive parts of DashboardLayout.js (Client Component) into layout.js (Server Component) to reduce the amount of component JavaScript you send to the client.


Step 3: Migrating next/head
In the pages directory, the next/head React component is used to manage <head> HTML elements such as title and meta . In the app directory, next/head is replaced with the new built-in SEO support.
Before:
pages/index.tsx import Head from 'next/head'

export default function Page() {
  return (
    <>
      <Head>
        <title>My page title</title>
      </Head>
    </>
  )
}
After:
app/page.tsx import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'My Page Title',
}

export default function Page() {
  return '...'
}
See all metadata options.
Step 4: Migrating Pages


}
See all metadata options.
Step 4: Migrating Pages
Pages in the app directory are Server Components by default. This is different from the pages directory where pages are Client Components.
Data fetching has changed in app. getServerSideProps, getStaticProps and getInitialProps have been replaced for a simpler API.
The app directory uses nested folders to define routes and a special page.js file to make a route segment publicly accessible.
pages Directoryapp DirectoryRouteindex.jspage.js/about.jsabout/page.js/aboutblog/[slug].jsblog/[slug]/page.js/blog/post-1
We recommend breaking down the migration of a page into two main steps:
Step 1: Move the default exported Page Component into a new Client Component.
Step 2: Import the new Client Component into a new page.js file inside the app directory.


Good to know: This is the easiest migration path because it has the most comparable behavior to the pages directory.
Step 1: Create a new Client Component
Create a new separate file inside the app directory (i.e. app/home-page.tsx or similar) that exports a Client Component. To define Client Components, add the 'use client' directive to the top of the file (before any imports).
Move the default exported page component from pages/index.js to app/home-page.tsx.
app/home-page.tsx 'use client'

// This is a Client Component. It receives data as props and
// has access to state and effects just like Page components
// in the `pages` directory.
export default function HomePage({ recentPosts }) {
  return (

      {recentPosts.map((post) => (

{post.title}

      ))}


{post.title}

      ))}

  )
}
Step 2: Create a new page
Create a new app/page.tsx file inside the app directory. This is a Server Component by default.
Import the home-page.tsx Client Component into the page.
If you were fetching data in pages/index.js, move the data fetching logic directly into the Server Component using the new data fetching APIs. See the data fetching upgrade guide for more details.
app/page.tsx // Import your Client Component
import HomePage from './home-page'

async function getPosts() {
  const res = await fetch('https://...')
  const posts = await res.json()
  return posts
}

export default async function Page() {
  // Fetch data directly in a Server Component
  const recentPosts = await getPosts()


// Fetch data directly in a Server Component
  const recentPosts = await getPosts()
  // Forward fetched data to your Client Component
  return <HomePage recentPosts={recentPosts} />
}
If your previous page used useRouter, you'll need to update to the new routing hooks. Learn more.
Start your development server and visit http://localhost:3000. You should see your existing index route, now served through the app directory.
Step 5: Migrating Routing Hooks
A new router has been added to support the new behavior in the app directory.
In app, you should use the three new hooks imported from next/navigation: useRouter(), usePathname(), and useSearchParams().
The new useRouter hook is imported from next/navigation and has different behavior to the useRouter hook in pages which is imported from next/router.


The useRouter hook imported from next/router is not supported in the app directory but can continue to be used in the pages directory.
The new useRouter does not return the pathname string. Use the separate usePathname hook instead.
The new useRouter does not return the query object. Use the separate useSearchParams hook instead.
You can use useSearchParams and usePathname together to listen to page changes. See the Router Events section for more details.
These new hooks are only supported in Client Components. They cannot be used in Server Components.
app/example-client-component.tsx 'use client'

import { useRouter, usePathname, useSearchParams } from 'next/navigation'

export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()

  // ...
}
In addition, the new useRouter hook has the following changes:


// ...
}
In addition, the new useRouter hook has the following changes:
isFallback has been removed because fallback has been replaced.
The locale, locales, defaultLocales, domainLocales values have been removed because built-in i18n Next.js features are no longer necessary in the app directory. Learn more about i18n.
basePath has been removed. The alternative will not be part of useRouter. It has not yet been implemented.
asPath has been removed because the concept of as has been removed from the new router.
isReady has been removed because it is no longer necessary. During static rendering, any component that uses the useSearchParams() hook will skip the prerendering step and instead be rendered on the client at runtime.
View the useRouter() API reference.
Step 6: Migrating Data Fetching Methods


View the useRouter() API reference.
Step 6: Migrating Data Fetching Methods
The pages directory uses getServerSideProps and getStaticProps to fetch data for pages. Inside the app directory, these previous data fetching functions are replaced with a simpler API built on top of fetch() and async React Server Components.
app/page.tsx export default async function Page() {
  // This request should be cached until manually invalidated.
  // Similar to `getStaticProps`.
  // `force-cache` is the default and can be omitted.
  const staticData = await fetch(`https://...`, { cache: 'force-cache' })

  // This request should be refetched on every request.
  // Similar to `getServerSideProps`.
  const dynamicData = await fetch(`https://...`, { cache: 'no-store' })


// This request should be cached with a lifetime of 10 seconds.
  // Similar to `getStaticProps` with the `revalidate` option.
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  })

  return <div>...</div>
}
Server-side Rendering (getServerSideProps)
In the pages directory, getServerSideProps is used to fetch data on the server and forward props to the default exported React component in the file. The initial HTML for the page is prerendered from the server, followed by "hydrating" the page in the browser (making it interactive).
pages/dashboard.js // `pages` directory

export async function getServerSideProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()


const projects = await res.json()

  return { props: { projects } }
}

export default function Dashboard({ projects }) {
  return (

      {projects.map((project) => (

{project.name}

      ))}

  )
}
In the app directory, we can colocate our data fetching inside our React components using Server Components. This allows us to send less JavaScript to the client, while maintaining the rendered HTML from the server.
By setting the cache option to no-store, we can indicate that the fetched data should never be cached. This is similar to getServerSideProps in the pages directory.
app/dashboard/page.tsx // `app` directory

// This function can be named anything
async function getProjects() {


// This function can be named anything
async function getProjects() {
  const res = await fetch(`https://...`, { cache: 'no-store' })
  const projects = await res.json()

  return projects
}

export default async function Dashboard() {
  const projects = await getProjects()

  return (

      {projects.map((project) => (

{project.name}

      ))}

  )
}
Accessing Request Object
In the pages directory, you can retrieve request-based data based on the Node.js HTTP API.
For example, you can retrieve the req object from getServerSideProps and use it to retrieve the request's cookies and headers.
pages/index.js // `pages` directory

export async function getServerSideProps({ req, query }) {


export async function getServerSideProps({ req, query }) {
  const authHeader = req.getHeaders()['authorization'];
  const theme = req.cookies['theme'];

  return { props: { ... }}
}

export default function Page(props) {
  return ...
}
The app directory exposes new read-only functions to retrieve request data:
headers(): Based on the Web Headers API, and can be used inside Server Components to retrieve request headers.
cookies(): Based on the Web Cookies API, and can be used inside Server Components to retrieve cookies.
app/page.tsx // `app` directory
import { cookies, headers } from 'next/headers'

async function getData() {
  const authHeader = headers().get('authorization')

  return '...'
}

export default async function Page() {


return '...'
}

export default async function Page() {
  // You can use `cookies()` or `headers()` inside Server Components
  // directly or in your data fetching function
  const theme = cookies().get('theme')
  const data = await getData()
  return '...'
}
Static Site Generation (getStaticProps)
In the pages directory, the getStaticProps function is used to pre-render a page at build time. This function can be used to fetch data from an external API or directly from a database, and pass this data down to the entire page as it's being generated during the build.
pages/index.js // `pages` directory

export async function getStaticProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()

  return { props: { projects } }
}


return { props: { projects } }
}

export default function Index({ projects }) {
  return projects.map((project) =>
{project.name}
)
}
In the app directory, data fetching with fetch() will default to cache: 'force-cache', which will cache the request data until manually invalidated. This is similar to getStaticProps in the pages directory.
app/page.js // `app` directory

// This function can be named anything
async function getProjects() {
  const res = await fetch(`https://...`)
  const projects = await res.json()

  return projects
}

export default async function Index() {
  const projects = await getProjects()

  return projects.map((project) =>
{project.name}
)
}
Dynamic paths (getStaticPaths)


}
Dynamic paths (getStaticPaths)
In the pages directory, the getStaticPaths function is used to define the dynamic paths that should be pre-rendered at build time.
pages/posts/[id].js // `pages` directory
import PostLayout from '@/components/post-layout'

export async function getStaticPaths() {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
  }
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  return { props: { post } }
}

export default function Post({ post }) {
  return <PostLayout post={post} />
}


return <PostLayout post={post} />
}
In the app directory, getStaticPaths is replaced with generateStaticParams.
generateStaticParams behaves similarly to getStaticPaths, but has a simplified API for returning route parameters and can be used inside layouts. The return shape of generateStaticParams is an array of segments instead of an array of nested param objects or a string of resolved paths.
app/posts/[id]/page.js // `app` directory
import PostLayout from '@/components/post-layout'

export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }]
}

async function getPost(params) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  return post
}

export default async function Post({ params }) {


return post
}

export default async function Post({ params }) {
  const post = await getPost(params)

  return <PostLayout post={post} />
}
Using the name generateStaticParams is more appropriate than getStaticPaths for the new model in the app directory. The get prefix is replaced with a more descriptive generate, which sits better alone now that getStaticProps and getServerSideProps are no longer necessary. The Paths suffix is replaced by Params, which is more appropriate for nested routing with multiple dynamic segments.
Replacing fallback
In the pages directory, the fallback property returned from getStaticPaths is used to define the behavior of a page that isn't pre-rendered at build time. This property can be set to true to show a fallback page while the page is being generated, false to show a 404 page, or blocking to generate the page at request time.


pages/posts/[id].js // `pages` directory

export async function getStaticPaths() {
  return {
    paths: [],
    fallback: 'blocking'
  };
}

export async function getStaticProps({ params }) {
  ...
}

export default function Post({ post }) {
  return ...
}
In the app directory the config.dynamicParams property controls how params outside of generateStaticParams are handled:
true: (default) Dynamic segments not included in generateStaticParams are generated on demand.
false: Dynamic segments not included in generateStaticParams will return a 404.
This replaces the fallback: true | false | 'blocking' option of getStaticPaths in the pages directory. The fallback: 'blocking' option is not included in dynamicParams because the difference between 'blocking' and true is negligible with streaming.
app/posts/[id]/page.js // `app` directory


app/posts/[id]/page.js // `app` directory

export const dynamicParams = true;

export async function generateStaticParams() {
  return [...]
}

async function getPost(params) {
  ...
}

export default async function Post({ params }) {
  const post = await getPost(params);

  return ...
}
With dynamicParams set to true (the default), when a route segment is requested that hasn't been generated, it will be server-rendered and cached as static data on success.
Incremental Static Regeneration (getStaticProps with revalidate)
In the pages directory, the getStaticProps function allows you to add a revalidate field to automatically regenerate a page after a certain amount of time. This is called Incremental Static Regeneration (ISR) and helps you update static content without redeploying.
pages/index.js // `pages` directory


pages/index.js // `pages` directory

export async function getStaticProps() {
  const res = await fetch(`https://.../posts`)
  const posts = await res.json()

  return {
    props: { posts },
    revalidate: 60,
  }
}

export default function Index({ posts }) {
  return (
    <Layout>
      <PostList posts={posts} />
    </Layout>
  )
}
In the app directory, data fetching with fetch() can use revalidate, which will cache the request for the specified amount of seconds.
app/page.js // `app` directory

async function getPosts() {
  const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
  const data = await res.json()

  return data.posts
}


const data = await res.json()

  return data.posts
}

export default async function PostList() {
  const posts = await getPosts()

  return posts.map((post) => <div>{post.name}</div>)
}
API Routes
API Routes continue to work in the pages/api directory without any changes. However, they have been replaced by Route Handlers in the app directory.
Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs.
app/api/route.ts export async function GET(request: Request) {}
Good to know: If you previously used API routes to call an external API from the client, you can now use Server Components instead to securely fetch data. Learn more about data fetching.
Step 7: Styling


Step 7: Styling
In the pages directory, global stylesheets are restricted to only pages/_app.js. With the app directory, this restriction has been lifted. Global styles can be added to any layout, page, or component.
CSS Modules
Tailwind CSS
Global Styles
CSS-in-JS
External Stylesheets
Sass
Tailwind CSS
If you're using Tailwind CSS, you'll need to add the app directory to your tailwind.config.js file:
tailwind.config.js module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Add this line
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
}
You'll also need to import your global styles in your app/layout.js file:


}
You'll also need to import your global styles in your app/layout.js file:
app/layout.js import '../styles/globals.css'

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
Learn more about styling with Tailwind CSS
Codemods
Next.js provides Codemod transformations to help upgrade your codebase when a feature is deprecated. See Codemods for more information.


Script Optimization
Layout Scripts
To load a third-party script for multiple routes, import next/script and include the script directly in your layout component:app/dashboard/layout.tsx import Script from 'next/script'

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <>
      <section>{children}</section>
      <Script src="https://example.com/script.js" />
    </>
  )
}The third-party script is fetched when the folder route (e.g. dashboard/page.js) or any nested route (e.g. dashboard/settings/page.js) is accessed by the user. Next.js will ensure the script will only load once, even if a user navigates between multiple routes in the same layout.
Application Scripts


Application Scripts
To load a third-party script for all routes, import next/script and include the script directly in your root layout:app/layout.tsx import Script from 'next/script'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
      <Script src="https://example.com/script.js" />
    </html>
  )
}
This script will load and execute when any route in your application is accessed. Next.js will ensure the script will only load once, even if a user navigates between multiple pages.
Recommendation: We recommend only including third-party scripts in specific pages or layouts in order to minimize any unnecessary impact to performance.
Strategy


Strategy
Although the default behavior of next/script allows you load third-party scripts in any page or layout, you can fine-tune its loading behavior by using the strategy property:
beforeInteractive: Load the script before any Next.js code and before any page hydration occurs.
afterInteractive: (default) Load the script early but after some hydration on the page occurs.
lazyOnload: Load the script later during browser idle time.
worker: (experimental) Load the script in a web worker.
Refer to the next/script API reference documentation to learn more about each strategy and their use cases.
Offloading Scripts To A Web Worker (Experimental)
Warning: The worker strategy is not yet stable and does not yet work with the app directory. Use with caution.
Scripts that use the worker strategy are offloaded and executed in a web worker with Partytown. This can improve the performance of your site by dedicating the main thread to the rest of your application code.


This strategy is still experimental and can only be used if the nextScriptWorkers flag is enabled in next.config.js:
next.config.js module.exports = {
  experimental: {
    nextScriptWorkers: true,
  },
}
Then, run next (normally npm run dev or yarn dev) and Next.js will guide you through the installation of the required packages to finish the setup:
Terminal npm run dev
You'll see instructions like these: Please install Partytown by running npm install @builder.io/partytown
Once setup is complete, defining strategy="worker" will automatically instantiate Partytown in your application and offload the script to a web worker.
pages/home.tsx import Script from 'next/script'

export default function Home() {
  return (
    <>
      <Script src="https://example.com/script.js" strategy="worker" />
    </>
  )
}


</>
  )
}
There are a number of trade-offs that need to be considered when loading a third-party script in a web worker. Please see Partytown's tradeoffs documentation for more information.
Inline Scripts
Inline scripts, or scripts not loaded from an external file, are also supported by the Script component. They can be written by placing the JavaScript within curly braces:
 <Script id="show-banner">
  {`document.getElementById('banner').classList.remove('hidden')`}
</Script>
Or by using the dangerouslySetInnerHTML property:
 <Script
  id="show-banner"
  dangerouslySetInnerHTML={{
    __html: `document.getElementById('banner').classList.remove('hidden')`,
  }}
/>
Warning: An id property must be assigned for inline scripts in order for Next.js to track and optimize the script.
Executing Additional Code


Executing Additional Code
Event handlers can be used with the Script component to execute additional code after a certain event occurs:
onLoad: Execute code after the script has finished loading.
onReady: Execute code after the script has finished loading and every time the component is mounted.
onError: Execute code if the script fails to load.
These handlers will only work when next/script is imported and used inside of a Client Component where "use client" is defined as the first line of code:app/page.tsx 'use client'

import Script from 'next/script'

export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/script.js"
        onLoad={() => {
          console.log('Script has loaded')
        }}
      />
    </>
  )
}Refer to the next/script API reference to learn more about each event handler and view examples.


}Refer to the next/script API reference to learn more about each event handler and view examples.
Additional Attributes
There are many DOM attributes that can be assigned to a <script> element that are not used by the Script component, like nonce or custom data attributes. Including any additional attributes will automatically forward it to the final, optimized <script> element that is included in the HTML.
app/page.tsx import Script from 'next/script'

export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/script.js"
        id="example-script"
        nonce="XUENAJFW"
        data-test="script"
      />
    </>
  )
}


Static Assets
Next.js can serve static files, like images, under a folder called public in the root directory. Files inside public can then be referenced by your code starting from the base URL (/).
For example, if you add me.png inside public, the following code will access the image:
Avatar.js import Image from 'next/image'

export function Avatar() {
  return <Image src="/me.png" alt="me" width="64" height="64" />
}
For static metadata files, such as robots.txt, favicon.ico, etc, you should use special metadata files inside the app folder.
Good to know:
The directory must be named public. The name cannot be changed and it's the only directory used to serve static assets.


Only assets that are in the public directory at build time will be served by Next.js. Files added at runtime won't be available. We recommend using a third-party service like AWS S3 for persistent file storage.


Lazy Loading
Lazy loading in Next.js helps improve the initial loading performance of an application by decreasing the amount of JavaScript needed to render a route.
It allows you to defer loading of Client Components and imported libraries, and only include them in the client bundle when they're needed. For example, you might want to defer loading a modal until a user clicks to open it.
There are two ways you can implement lazy loading in Next.js:
Using Dynamic Imports with next/dynamic
Using React.lazy() with Suspense
By default, Server Components are automatically code split, and you can use streaming to progressively send pieces of UI from the server to the client. Lazy loading applies to Client Components.
next/dynamic
next/dynamic is a composite of React.lazy() and Suspense. It behaves the same way in the app and pages directories to allow for incremental migration.
Examples
Importing Client Components
app/page.js 'use client'


Examples
Importing Client Components
app/page.js 'use client'

import { useState } from 'react'
import dynamic from 'next/dynamic'

// Client Components:
const ComponentA = dynamic(() => import('../components/A'))
const ComponentB = dynamic(() => import('../components/B'))
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })

export default function ClientComponentExample() {
  const [showMore, setShowMore] = useState(false)

  return (
    <div>
      {/* Load immediately, but in a separate client bundle */}
      <ComponentA />

      {/* Load on demand, only when/if the condition is met */}
      {showMore && <ComponentB />}
      <button onClick={() => setShowMore(!showMore)}>Toggle</button>


<button onClick={() => setShowMore(!showMore)}>Toggle</button>

      {/* Load only on the client side */}
      <ComponentC />
    </div>
  )
}Skipping SSR
When using React.lazy() and Suspense, Client Components will be pre-rendered (SSR) by default.If you want to disable pre-rendering for a Client Component, you can use the ssr option set to false: const ComponentC = dynamic(() => import('../components/C'), { ssr: false })Importing Server Components
If you dynamically import a Server Component, only the Client Components that are children of the Server Component will be lazy-loaded - not the Server Component itself.app/page.js import dynamic from 'next/dynamic'

// Server Component:
const ServerComponent = dynamic(() => import('../components/ServerComponent'))

export default function ServerComponentExample() {
  return (
    <div>


export default function ServerComponentExample() {
  return (

      <ServerComponent />

  )
}Loading External Libraries
External libraries can be loaded on demand using the import() function. This example uses the external library fuse.js for fuzzy search. The module is only loaded on the client after the user types in the search input.app/page.js 'use client'

import { useState } from 'react'

const names = ['Tim', 'Joe', 'Bel', 'Lee']

export default function Page() {
  const [results, setResults] = useState()

  return (

      <input
        type="text"
        placeholder="Search"
        onChange={async (e) => {
          const { value } = e.currentTarget
          // Dynamically load fuse.js


const { value } = e.currentTarget
          // Dynamically load fuse.js
          const Fuse = (await import('fuse.js')).default
          const fuse = new Fuse(names)

          setResults(fuse.search(value))
        }}
      />

Results: {JSON.stringify(results, null, 2)}

  )
}Adding a custom loading component
app/page.js import dynamic from 'next/dynamic'

const WithCustomLoading = dynamic(
  () => import('../components/WithCustomLoading'),
  {
    loading: () =>
Loading...
,
  }
)

export default function Page() {
  return (

      {/* The loading component will be rendered while  <WithCustomLoading/> is loading */}
      <WithCustomLoading />

  )


<WithCustomLoading />

  )
}Importing Named Exports
To dynamically import a named export, you can return it from the Promise returned by import() function:components/hello.js 'use client'

export function Hello() {
  return
Hello!

}app/page.js import dynamic from 'next/dynamic'

const ClientComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod.Hello)
)


Font Optimization
next/font will automatically optimize your fonts (including custom fonts) and remove external network requests for improved privacy and performance.
🎥 Watch: Learn more about how to use next/font → YouTube (6 minutes).
next/font includes built-in automatic self-hosting for any font file. This means you can optimally load web fonts with zero layout shift, thanks to the underlying CSS size-adjust property used.
This new font system also allows you to conveniently use all Google Fonts with performance and privacy in mind. CSS and font files are downloaded at build time and self-hosted with the rest of your static assets. No requests are sent to Google by the browser.
Google Fonts
Automatically self-host any Google Font. Fonts are included in the deployment and served from the same domain as your deployment. No requests are sent to Google by the browser.


Get started by importing the font you would like to use from next/font/google as a function. We recommend using variable fonts for the best performance and flexibility.
app/layout.tsx import { Inter } from 'next/font/google'

// If loading a variable font, you don't need to specify the font weight
const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
})

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}If you can't use a variable font, you will need to specify a weight:app/layout.tsx import { Roboto } from 'next/font/google'

const roboto = Roboto({
  weight: '400',


const roboto = Roboto({
  weight: '400',
  subsets: ['latin'],
  display: 'swap',
})

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={roboto.className}>
      <body>{children}</body>
    </html>
  )
}
You can specify multiple weights and/or styles by using an array:
app/layout.js const roboto = Roboto({
  weight: ['400', '700'],
  style: ['normal', 'italic'],
  subsets: ['latin'],
  display: 'swap',
})
Good to know: Use an underscore (_) for font names with multiple words. E.g. Roboto Mono should be imported as Roboto_Mono.
Specifying a subset


Specifying a subset
Google Fonts are automatically subset. This reduces the size of the font file and improves performance. You'll need to define which of these subsets you want to preload. Failing to specify any subsets while preload is true will result in a warning.
This can be done by adding it to the function call:
app/layout.tsx const inter = Inter({ subsets: ['latin'] })
View the Font API Reference for more information.
Using Multiple Fonts
You can import and use multiple fonts in your application. There are two approaches you can take.
The first approach is to create a utility function that exports a font, imports it, and applies its className where needed. This ensures the font is preloaded only when it's rendered:
app/fonts.ts import { Inter, Roboto_Mono } from 'next/font/google'

export const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
})


subsets: ['latin'],
  display: 'swap',
})

export const roboto_mono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
})
app/layout.tsx import { inter } from './fonts'

export default function Layout({ children }: { children: React.ReactNode }) {
  return (

{children}

  )
}app/page.tsx import { roboto_mono } from './fonts'

export default function Page() {
  return (
    <>

My page

    </>
  )
}


</>
  )
}
In the example above, Inter will be applied globally, and Roboto Mono can be imported and applied as needed.
Alternatively, you can create a CSS variable and use it with your preferred CSS solution:
app/layout.tsx import { Inter, Roboto_Mono } from 'next/font/google'
import styles from './global.css'

const inter = Inter({
  subsets: ['latin'],
  variable: '--font-inter',
  display: 'swap',
})

const roboto_mono = Roboto_Mono({
  subsets: ['latin'],
  variable: '--font-roboto-mono',
  display: 'swap',
})

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (


children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={`${inter.variable} ${roboto_mono.variable}`}>

My App

{children}

  )
}
app/global.css html {
  font-family: var(--font-inter);
}

h1 {
  font-family: var(--font-roboto-mono);
}
In the example above, Inter will be applied globally, and any
 tags will be styled with Roboto Mono.
Recommendation: Use multiple fonts conservatively since each new font is an additional resource the client has to download.
Local Fonts


Local Fonts
Import next/font/local and specify the src of your local font file. We recommend using variable fonts for the best performance and flexibility.
app/layout.tsx import localFont from 'next/font/local'

// Font files can be colocated inside of `app`
const myFont = localFont({
  src: './my-font.woff2',
  display: 'swap',
})

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={myFont.className}>
      <body>{children}</body>
    </html>
  )
}
If you want to use multiple files for a single font family, src can be an array:
 const roboto = localFont({
  src: [
    {
      path: './Roboto-Regular.woff2',


src: [
    {
      path: './Roboto-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: './Roboto-Italic.woff2',
      weight: '400',
      style: 'italic',
    },
    {
      path: './Roboto-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
    {
      path: './Roboto-BoldItalic.woff2',
      weight: '700',
      style: 'italic',
    },
  ],
})
View the Font API Reference for more information.
With Tailwind CSS
next/font can be used with Tailwind CSS through a CSS variable.


With Tailwind CSS
next/font can be used with Tailwind CSS through a CSS variable.
In the example below, we use the font Inter from next/font/google (you can use any font from Google or Local Fonts). Load your font with the variable option to define your CSS variable name and assign it to inter. Then, use inter.variable to add the CSS variable to your HTML document.
app/layout.tsx import { Inter, Roboto_Mono } from 'next/font/google'

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
})

const roboto_mono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-roboto-mono',
})

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {


children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={`${inter.variable} ${roboto_mono.variable}`}>
      <body>{children}</body>
    </html>
  )
}
Finally, add the CSS variable to your Tailwind CSS config:
tailwind.config.js /** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
    './app/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      fontFamily: {
        sans: ['var(--font-inter)'],
        mono: ['var(--font-roboto-mono)'],
      },
    },
  },


},
    },
  },
  plugins: [],
}
You can now use the font-sans and font-mono utility classes to apply the font to your elements.
Preloading
When a font function is called on a page of your site, it is not globally available and preloaded on all routes. Rather, the font is only preloaded on the related routes based on the type of file where it is used:
If it's a unique page, it is preloaded on the unique route for that page.
If it's a layout, it is preloaded on all the routes wrapped by the layout.
If it's the root layout, it is preloaded on all routes.
Reusing fonts


If it's the root layout, it is preloaded on all routes.
Reusing fonts
Every time you call the localFont or Google font function, that font is hosted as one instance in your application. Therefore, if you load the same font function in multiple files, multiple instances of the same font are hosted. In this situation, it is recommended to do the following:
Call the font loader function in one shared file
Export it as a constant
Import the constant in each file where you would like to use this font


OpenTelemetry
Good to know: This feature is experimental, you need to explicitly opt-in by providing experimental.instrumentationHook = true; in your next.config.js.
Observability is crucial for understanding and optimizing the behavior and performance of your Next.js app.
As applications become more complex, it becomes increasingly difficult to identify and diagnose issues that may arise. By leveraging observability tools, such as logging and metrics, developers can gain insights into their application's behavior and identify areas for optimization. With observability, developers can proactively address issues before they become major problems and provide a better user experience. Therefore, it is highly recommended to use observability in your Next.js applications to improve performance, optimize resources, and enhance user experience.
We recommend using OpenTelemetry for instrumenting your apps.
It's a platform-agnostic way to instrument apps that allows you to change your observability provider without changing your code.


Read Official OpenTelemetry docs for more information about OpenTelemetry and how it works.
This documentation uses terms like Span, Trace or Exporter throughout this doc, all of which can be found in the OpenTelemetry Observability Primer.
Next.js supports OpenTelemetry instrumentation out of the box, which means that we already instrumented Next.js itself.
When you enable OpenTelemetry we will automatically wrap all your code like getStaticProps in spans with helpful attributes.
Good to know: We currently support OpenTelemetry bindings only in serverless functions.
We don't provide any for edge or client side code.
Getting Started
OpenTelemetry is extensible but setting it up properly can be quite verbose.
That's why we prepared a package @vercel/otel that helps you get started quickly.
It's not extensible and you should configure OpenTelemetry manually if you need to customize your setup.
Using @vercel/otel


Using @vercel/otel
To get started, you must install @vercel/otel:
Terminal npm install @vercel/otel
Next, create a custom instrumentation.ts (or .js) file in the root directory of the project (or inside src folder if using one):
your-project/instrumentation.ts import { registerOTel } from '@vercel/otel'

export function register() {
  registerOTel('next-app')
}
Good to know
The instrumentation file should be in the root of your project and not inside the app or pages directory. If you're using the src folder, then place the file inside src alongside pages and app.
If you use the pageExtensions config option to add a suffix, you will also need to update the instrumentation filename to match.
We have created a basic with-opentelemetry example that you can use.
Manual OpenTelemetry configuration


Manual OpenTelemetry configuration
If our wrapper @vercel/otel doesn't suit your needs, you can configure OpenTelemetry manually.
Firstly you need to install OpenTelemetry packages:
Terminal npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http
Now you can initialize NodeSDK in your instrumentation.ts.
OpenTelemetry APIs are not compatible with edge runtime, so you need to make sure that you are importing them only when process.env.NEXT_RUNTIME === 'nodejs'. We recommend creating a new file instrumentation.node.ts which you conditionally import only when using node:
instrumentation.ts export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation.node.ts')
  }
}


await import('./instrumentation.node.ts')
  }
}
instrumentation.node.ts import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'next-app',
  }),
  spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()
Doing this is equivalent to using @vercel/otel, but it's possible to modify and extend.


For example, you could use @opentelemetry/exporter-trace-otlp-grpc instead of @opentelemetry/exporter-trace-otlp-http or you can specify more resource attributes.
Testing your instrumentation
You need an OpenTelemetry collector with a compatible backend to test OpenTelemetry traces locally.
We recommend using our OpenTelemetry dev environment.
If everything works well you should be able to see the root server span labeled as GET /requested/pathname.
All other spans from that particular trace will be nested under it.
Next.js traces more spans than are emitted by default.
To see more spans, you must set NEXT_OTEL_VERBOSE=1.
Deployment
Using OpenTelemetry Collector
When you are deploying with OpenTelemetry Collector, you can use @vercel/otel.
It will work both on Vercel and when self-hosted.
Deploying on Vercel


Deploying on Vercel
We made sure that OpenTelemetry works out of the box on Vercel.
Follow Vercel documentation to connect your project to an observability provider.
Self-hosting
Deploying to other platforms is also straightforward. You will need to spin up your own OpenTelemetry Collector to receive and process the telemetry data from your Next.js app.
To do this, follow the OpenTelemetry Collector Getting Started guide, which will walk you through setting up the collector and configuring it to receive data from your Next.js app.
Once you have your collector up and running, you can deploy your Next.js app to your chosen platform following their respective deployment guides.
Custom Exporters
We recommend using OpenTelemetry Collector.
If that is not possible on your platform, you can use a custom OpenTelemetry exporter with manual OpenTelemetry configuration
Custom Spans
You can add a custom span with OpenTelemetry APIs.


Custom Spans
You can add a custom span with OpenTelemetry APIs.
Terminal npm install @opentelemetry/api
The following example demonstrates a function that fetches GitHub stars and adds a custom fetchGithubStars span to track the fetch request's result:
 import { trace } from '@opentelemetry/api'

export async function fetchGithubStars() {
  return await trace
    .getTracer('nextjs-example')
    .startActiveSpan('fetchGithubStars', async (span) => {
      try {
        return await getValue()
      } finally {
        span.end()
      }
    })
}
The register function will execute before your code runs in a new environment.
You can start creating new spans, and they should be correctly added to the exported trace.
Default Spans in Next.js
Next.js automatically instruments several spans for you to provide useful insights into your application's performance.


Next.js automatically instruments several spans for you to provide useful insights into your application's performance.
Attributes on spans follow OpenTelemetry semantic conventions. We also add some custom attributes under the next namespace:
next.span_name - duplicates span name
next.span_type - each span type has a unique identifier
next.route - The route pattern of the request (e.g., /[param]/user).
next.page
This is an internal value used by an app router.
You can think about it as a route to a special file (like page.ts, layout.ts, loading.ts and others)
It can be used as a unique identifier only when paired with next.route because /layout can be used to identify both /(groupA)/layout.ts and /(groupB)/layout.ts
[http.method] [next.route]
next.span_type: BaseServer.handleRequest


[http.method] [next.route]
next.span_type: BaseServer.handleRequest
This span represents the root span for each incoming request to your Next.js application. It tracks the HTTP method, route, target, and status code of the request.
Attributes:
Common HTTP attributes
http.method
http.status_code
Server HTTP attributes
http.route
http.target
next.span_name
next.span_type
next.route
render route (app) [next.route]
next.span_type: AppRender.getBodyResult.
This span represents the process of rendering a route in the app router.
Attributes:
next.span_name
next.span_type
next.route
fetch [http.method] [http.url]
next.span_type: AppRender.fetch
This span represents the fetch request executed in your code.
Attributes:
Common HTTP attributes
http.method
Client HTTP attributes
http.url
net.peer.name
net.peer.port (only if specified)
next.span_name


http.url
net.peer.name
net.peer.port (only if specified)
next.span_name
next.span_type
executing api route (app) [next.route]
next.span_type: AppRouteRouteHandlers.runHandler.
This span represents the execution of an API route handler in the app router.
Attributes:
next.span_name
next.span_type
next.route
getServerSideProps [next.route]
next.span_type: Render.getServerSideProps.
This span represents the execution of getServerSideProps for a specific route.
Attributes:
next.span_name
next.span_type
next.route
getStaticProps [next.route]
next.span_type: Render.getStaticProps.
This span represents the execution of getStaticProps for a specific route.
Attributes:
next.span_name
next.span_type
next.route
render route (pages) [next.route]
next.span_type: Render.renderDocument.


render route (pages) [next.route]
next.span_type: Render.renderDocument.
This span represents the process of rendering the document for a specific route.
Attributes:
next.span_name
next.span_type
next.route
generateMetadata [next.page]
next.span_type: ResolveMetadata.generateMetadata.
This span represents the process of generating metadata for a specific page (a single route can have multiple of these spans).
Attributes:
next.span_name
next.span_type
next.page


Image Optimization
Examples
Image Component
According to Web Almanac, images account for a huge portion of the typical website’s page weight and can have a sizable impact on your website's LCP performance.
The Next.js Image component extends the HTML <img> element with features for automatic image optimization:
Size Optimization: Automatically serve correctly sized images for each device, using modern image formats like WebP and AVIF.
Visual Stability: Prevent layout shift automatically when images are loading.
Faster Page Loads: Images are only loaded when they enter the viewport using native browser lazy loading, with optional blur-up placeholders.
Asset Flexibility: On-demand image resizing, even for images stored on remote servers
🎥 Watch: Learn more about how to use next/image → YouTube (9 minutes).
Usage
 import Image from 'next/image'
You can then define the src for your image (either local or remote).
Local Images


You can then define the src for your image (either local or remote).
Local Images
To use a local image, import your .jpg, .png, or .webp image files.
Next.js will automatically determine the width and height of your image based on the imported file. These values are used to prevent Cumulative Layout Shift while your image is loading.
app/page.js import Image from 'next/image'
import profilePic from './me.png'

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
    />
  )
}


/>
  )
}
Warning: Dynamic await import() or require() are not supported. The import must be static so it can be analyzed at build time.
Remote Images
To use a remote image, the src property should be a URL string.
Since Next.js does not have access to remote files during the build process, you'll need to provide the width, height and optional blurDataURL props manually.
The width and height attributes are used to infer the correct aspect ratio of image and avoid layout shift from the image loading in. The width and height do not determine the rendered size of the image file. Learn more about Image Sizing.
app/page.js import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="https://s3.amazonaws.com/my-bucket/profile.png"
      alt="Picture of the author"
      width={500}
      height={500}


width={500}
      height={500}
    />
  )
}
To safely allow optimizing images, define a list of supported URL patterns in next.config.js. Be as specific as possible to prevent malicious usage. For example, the following configuration will only allow images from a specific AWS S3 bucket:
next.config.js module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 's3.amazonaws.com',
        port: '',
        pathname: '/my-bucket/**',
      },
    ],
  },
}
Learn more about remotePatterns configuration. If you want to use relative URLs for the image src, use a loader.
Domains


Domains
Sometimes you may want to optimize a remote image, but still use the built-in Next.js Image Optimization API. To do this, leave the loader at its default setting and enter an absolute URL for the Image src prop.
To protect your application from malicious users, you must define a list of remote hostnames you intend to use with the next/image component.
Learn more about remotePatterns configuration.
Loaders
Note that in the example earlier, a partial URL ("/me.png") is provided for a remote image. This is possible because of the loader architecture.
A loader is a function that generates the URLs for your image. It modifies the provided src, and generates multiple URLs to request the image at different sizes. These multiple URLs are used in the automatic srcset generation, so that visitors to your site will be served an image that is the right size for their viewport.


The default loader for Next.js applications uses the built-in Image Optimization API, which optimizes images from anywhere on the web, and then serves them directly from the Next.js web server. If you would like to serve your images directly from a CDN or image server, you can write your own loader function with a few lines of JavaScript.
You can define a loader per-image with the loader prop, or at the application level with the loaderFile configuration.
Priority
You should add the priority property to the image that will be the Largest Contentful Paint (LCP) element for each page. Doing so allows Next.js to specially prioritize the image for loading (e.g. through preload tags or priority hints), leading to a meaningful boost in LCP.


The LCP element is typically the largest image or text block visible within the viewport of the page. When you run next dev, you'll see a console warning if the LCP element is an <Image> without the priority property.
Once you've identified the LCP image, you can add the property like this:
app/page.js import Image from 'next/image'
import profilePic from '../public/me.png'

export default function Page() {
  return <Image src={profilePic} alt="Picture of the author" priority />
}
See more about priority in the next/image component documentation.
Image Sizing


}
See more about priority in the next/image component documentation.
Image Sizing
One of the ways that images most commonly hurt performance is through layout shift, where the image pushes other elements around on the page as it loads in. This performance problem is so annoying to users that it has its own Core Web Vital, called Cumulative Layout Shift. The way to avoid image-based layout shifts is to always size your images. This allows the browser to reserve precisely enough space for the image before it loads.
Because next/image is designed to guarantee good performance results, it cannot be used in a way that will contribute to layout shift, and must be sized in one of three ways:
Automatically, using a static import
Explicitly, by including a width and height property
Implicitly, by using fill which causes the image to expand to fill its parent element.
What if I don't know the size of my images?


What if I don't know the size of my images?
If you are accessing images from a source without knowledge of the images' sizes, there are several things you can do:
Use fill
The fill prop allows your image to be sized by its parent element. Consider using CSS to give the image's parent element space on the page along sizes prop to match any media query break points. You can also use object-fit with fill, contain, or cover, and object-position to define how the image should occupy that space.
Normalize your images
If you're serving images from a source that you control, consider modifying your image pipeline to normalize the images to a specific size.
Modify your API calls
If your application is retrieving image URLs using an API call (such as to a CMS), you may be able to modify the API call to return the image dimensions along with the URL.


If none of the suggested methods works for sizing your images, the next/image component is designed to work well on a page alongside standard <img> elements.
Styling
Styling the Image component is similar to styling a normal <img> element, but there are a few guidelines to keep in mind:
Use className or style, not styled-jsx.
In most cases, we recommend using the className prop. This can be an imported CSS Module, a global stylesheet, etc.
You can also use the style prop to assign inline styles.
You cannot use styled-jsx because it's scoped to the current component (unless you mark the style as global).
When using fill, the parent element must have position: relative
This is necessary for the proper rendering of the image element in that layout mode.
When using fill, the parent element must have display: block
This is the default for <div> elements but should be specified otherwise.
Examples
Responsive


This is the default for <div> elements but should be specified otherwise.
Examples
Responsive
 import Image from 'next/image'
import mountains from '../public/mountains.jpg'

export default function Responsive() {
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      <Image
        alt="Mountains"
        // Importing an image will
        // automatically set the width and height
        src={mountains}
        sizes="100vw"
        // Make the image display full width
        style={{
          width: '100%',
          height: 'auto',
        }}
      />
    </div>
  )
}
Fill Container
 import Image from 'next/image'
import mountains from '../public/mountains.jpg'

export default function Fill() {
  return (
    <div
      style={{


export default function Fill() {
  return (
    <div
      style={{
        display: 'grid',
        gridGap: '8px',
        gridTemplateColumns: 'repeat(auto-fit, minmax(400px, auto))',
      }}
    >
      <div style={{ position: 'relative', height: '400px' }}>
        <Image
          alt="Mountains"
          src={mountains}
          fill
          sizes="(min-width: 808px) 50vw, 100vw"
          style={{
            objectFit: 'cover', // cover, contain, none
          }}
        />
      </div>
      {/* And more images in the grid... */}
    </div>
  )
}
Background Image
 import Image from 'next/image'
import mountains from '../public/mountains.jpg'

export default function Background() {


import mountains from '../public/mountains.jpg'

export default function Background() {
  return (
    <Image
      alt="Mountains"
      src={mountains}
      placeholder="blur"
      quality={100}
      fill
      sizes="100vw"
      style={{
        objectFit: 'cover',
      }}
    />
  )
}
For examples of the Image component used with the various styles, see the Image Component Demo.
Other Properties
View all properties available to the next/image component.
Configuration
The next/image component and Next.js Image Optimization API can be configured in the next.config.js file. These configurations allow you to enable remote images, define custom image breakpoints, change caching behavior and more.
Read the full image configuration documentation for more information.


Analytics
Next.js Speed Insights allows you to analyze and measure the performance of
pages using different metrics.
You can start collecting your Real Experience Score with zero-configuration on Vercel deployments.
The rest of this documentation describes the built-in relayer Next.js Speed Insights uses.
Web Vitals
Web Vitals are a set of useful metrics that aim to capture the user
experience of a web page. The following web vitals are all included:
Time to First Byte (TTFB)
First Contentful Paint (FCP)
Largest Contentful Paint (LCP)
First Input Delay (FID)
Cumulative Layout Shift (CLS)
Interaction to Next Paint (INP) (experimental)


MetadataNext.js has a Metadata API that can be used to define your application metadata (e.g. meta and link tags inside your HTML head element) for improved SEO and web shareability.
There are two ways you can add metadata to your application:
Config-based Metadata: Export a static metadata object or a dynamic generateMetadata function in a layout.js or page.js file.
File-based Metadata: Add static or dynamically generated special files to route segments.
With both these options, Next.js will automatically generate the relevant <head> elements for your pages. You can also create dynamic OG images using the ImageResponse constructor.
Static Metadata
To define static metadata, export a Metadata object from a layout.js or static page.js file.
layout.tsx / page.tsx import { Metadata } from 'next'

export const metadata: Metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}


description: '...',
}

export default function Page() {}
For all the available options, see the API Reference.
Dynamic Metadata
You can use generateMetadata function to fetch metadata that requires dynamic values.
app/products/[id]/page.tsx import { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent?: ResolvingMetadata
): Promise<Metadata> {
  // read route params
  const id = params.id

  // fetch data
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // optionally access and extend (rather than replace) parent metadata


// optionally access and extend (rather than replace) parent metadata
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }: Props) {}
For all the available params, see the API Reference.
Good to know:
Both static and dynamic metadata through generateMetadata are only supported in Server Components.
When rendering a route, Next.js will automatically deduplicate fetch requests for the same data across generateMetadata, generateStaticParams, Layouts, Pages, and Server Components. React cache can be used if fetch is unavailable.


Next.js will wait for data fetching inside generateMetadata to complete before streaming UI to the client. This guarantees the first part of a streamed response includes <head> tags.
File-based metadata
These special files are available for metadata:
favicon.ico, apple-icon.jpg, and icon.jpg
opengraph-image.jpg and twitter-image.jpg
robots.txt
sitemap.xml
You can use these for static metadata, or you can programmatically generate these files with code.
For implementation and examples, see the Metadata Files API Reference and Dynamic Image Generation.
Behavior
File-based metadata has the higher priority and will override any config-based metadata.
Default Fields
There are two default meta tags that are always added even if a route doesn't define metadata:
The meta charset tag sets the character encoding for the website.
The meta viewport tag sets the viewport width and scale for the website to adjust for different devices.
 <meta charset="utf-8" />


<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
Good to know: You can overwrite the default viewport meta tag.
Ordering
Metadata is evaluated in order, starting from the root segment down to the segment closest to the final page.js segment. For example:
app/layout.tsx (Root Layout)
app/blog/layout.tsx (Nested Blog Layout)
app/blog/[slug]/page.tsx (Blog Page)
Merging
Following the evaluation order, Metadata objects exported from multiple segments in the same route are shallowly merged together to form the final metadata output of a route. Duplicate keys are replaced based on their ordering.
This means metadata with nested fields such as openGraph and robots that are defined in an earlier segment are overwritten by the last segment to define them.
Overwriting fields
app/layout.js export const metadata = {
  title: 'Acme',


app/layout.js export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/blog/page.js export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}

// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />
In the example above:
title from app/layout.js is replaced by title in app/blog/page.js.
All openGraph fields from app/layout.js are replaced in app/blog/page.js because app/blog/page.js sets openGraph metadata. Note the absence of openGraph.description.
If you'd like to share some nested fields between segments while overwriting others, you can pull them out into a separate variable:


app/shared-metadata.js export const openGraphImage = { images: ['http://...'] }
app/page.js import { openGraphImage } from './shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Home',
  },
}
app/about/page.js import { openGraphImage } from '../shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
}
In the example above, the OG image is shared between app/layout.js and app/about/page.js while the titles are different.
Inheriting fields
app/layout.js export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}


description: 'Acme is a...',
  },
}
app/about/page.js export const metadata = {
  title: 'About',
}

// Output:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />
Notes
title from app/layout.js is replaced by title in app/about/page.js.
All openGraph fields from app/layout.js are inherited in app/about/page.js because app/about/page.js doesn't set openGraph metadata.
Dynamic Image Generation
The ImageResponse constructor allows you to generate dynamic images using JSX and CSS. This is useful for creating social media images such as Open Graph images, Twitter cards, and more.
ImageResponse uses the Edge Runtime, and Next.js automatically adds the correct headers to cached images at the edge, helping improve performance and reducing recomputation.


To use it, you can import ImageResponse from next/server:
app/about/route.js import { ImageResponse } from 'next/server'

export const runtime = 'edge'

export async function GET() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          textAlign: 'center',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        Hello world!
      </div>
    ),
    {
      width: 1200,
      height: 600,
    }
  )
}


height: 600,
    }
  )
}
ImageResponse integrates well with other Next.js APIs, including Route Handlers and file-based Metadata. For example, you can use ImageResponse in a opengraph-image.tsx file to generate Open Graph images at build time or dynamically at request time.
ImageResponse supports common CSS properties including flexbox and absolute positioning, custom fonts, text wrapping, centering, and nested images. See the full list of supported CSS properties.
Good to know:
Examples are available in the Vercel OG Playground.
ImageResponse uses @vercel/og, Satori, and Resvg to convert HTML and CSS into PNG.
Only the Edge Runtime is supported. The default Node.js runtime will not work.
Only flexbox and a subset of CSS properties are supported. Advanced layouts (e.g. display: grid) will not work.


Maximum bundle size of 500KB. The bundle size includes your JSX, CSS, fonts, images, and any other assets. If you exceed the limit, consider reducing the size of any assets or fetching at runtime.
Only ttf, otf, and woff font formats are supported. To maximize the font parsing speed, ttf or otf are preferred over woff.
JSON-LD
JSON-LD is a format for structured data that can be used by search engines to understand your content. For example, you can use it to describe a person, an event, an organization, a movie, a book, a recipe, and many other types of entities.
Our current recommendation for JSON-LD is to render structured data as a <script> tag in your layout.js or page.js components. For example:
app/products/[id]/page.tsx export default async function Page({ params }) {
  const product = await getProduct(params.id)


const product = await getProduct(params.id)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }

  return (
    <section>
      {/* Add JSON-LD to your page */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}
You can validate and test your structured data with the Rich Results Test for Google or the generic Schema Markup Validator.
You can type your JSON-LD with TypeScript using community packages like schema-dts:
 import { Product, WithContext } from 'schema-dts'


import { Product, WithContext } from 'schema-dts'

const jsonLd: WithContext<Product> = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: 'Next.js Sticker',
  image: 'https://nextjs.org/imgs/sticker.png',
  description: 'Dynamic at the speed of static.',
}


Instrumentation
If you export a function named register from a instrumentation.ts (or .js) file in the root directory of your project (or inside the src folder if using one), we will call that function whenever a new Next.js server instance is bootstrapped.
Good to know
This feature is experimental. To use it, you must explicitly opt in by defining experimental.instrumentationHook = true; in your next.config.js.
The instrumentation file should be in the root of your project and not inside the app or pages directory. If you're using the src folder, then place the file inside src alongside pages and app.
If you use the pageExtensions config option to add a suffix, you will also need to update the instrumentation filename to match.
We have created a basic with-opentelemetry example that you can use.
When your register function is deployed, it will be called on each cold boot (but exactly once in each environment).


Sometimes, it may be useful to import a file in your code because of the side effects it will cause. For example, you might import a file that defines a set of global variables, but never explicitly use the imported file in your code. You would still have access to the global variables the package has declared.
You can import files with side effects in instrumentation.ts, which you might want to use in your register function as demonstrated in the following example:
your-project/instrumentation.ts import { init } from 'package-init'

export function register() {
  init()
}
However, we recommend importing files with side effects using import from within your register function instead. The following example demonstrates a basic usage of import in a register function:
your-project/instrumentation.ts export async function register() {
  await import('package-with-side-effect')
}


await import('package-with-side-effect')
}
By doing this, you can colocate all of your side effects in one place in your code, and avoid any unintended consequences from importing files.
We call register in all environments, so it's necessary to conditionally import any code that doesn't support both edge and nodejs. You can use the environment variable NEXT_RUNTIME to get the current environment. Importing an environment-specific code would look like this:
your-project/instrumentation.ts export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation-node')
  }

  if (process.env.NEXT_RUNTIME === 'edge') {
    await import('./instrumentation-edge')
  }
}


ESLint
Next.js provides an integrated ESLint experience out of the box. Add next lint as a script to package.json:
package.json {
  "scripts": {
    "lint": "next lint"
  }
}
Then run npm run lint or yarn lint:
Terminal yarn lint
If you don't already have ESLint configured in your application, you will be guided through the installation and configuration process.
Terminal yarn lint
You'll see a prompt like this:
? How would you like to configure ESLint?
❯ Strict (recommended)
Base
Cancel
One of the following three options can be selected:
Strict: Includes Next.js' base ESLint configuration along with a stricter Core Web Vitals rule-set. This is the recommended configuration for developers setting up ESLint for the first time.
.eslintrc.json {
  "extends": "next/core-web-vitals"
}


"extends": "next/core-web-vitals"
}
Base: Includes Next.js' base ESLint configuration.
.eslintrc.json {
  "extends": "next"
}
Cancel: Does not include any ESLint configuration. Only select this option if you plan on setting up your own custom ESLint configuration.
If either of the two configuration options are selected, Next.js will automatically install eslint and eslint-config-next as development dependencies in your application and create an .eslintrc.json file in the root of your project that includes your selected configuration.
You can now run next lint every time you want to run ESLint to catch errors. Once ESLint has been set up, it will also automatically run during every build (next build). Errors will fail the build, while warnings will not.
If you do not want ESLint to run during next build, refer to the documentation for Ignoring ESLint.


We recommend using an appropriate integration to view warnings and errors directly in your code editor during development.
ESLint Config
The default configuration (eslint-config-next) includes everything you need to have an optimal out-of-the-box linting experience in Next.js. If you do not have ESLint already configured in your application, we recommend using next lint to set up ESLint along with this configuration.
If you would like to use eslint-config-next along with other ESLint configurations, refer to the Additional Configurations section to learn how to do so without causing any conflicts.
Recommended rule-sets from the following ESLint plugins are all used within eslint-config-next:
eslint-plugin-react
eslint-plugin-react-hooks
eslint-plugin-next
This will take precedence over the configuration from next.config.js.
ESLint Plugin


This will take precedence over the configuration from next.config.js.
ESLint Plugin
Next.js provides an ESLint plugin, eslint-plugin-next, already bundled within the base configuration that makes it possible to catch common issues and problems in a Next.js application. The full set of rules is as follows:
 Enabled in the recommended configuration


RuleDescription@next/next/google-font-displayEnforce font-display behavior with Google Fonts.@next/next/google-font-preconnectEnsure preconnect is used with Google Fonts.@next/next/inline-script-idEnforce id attribute on next/script components with inline content.@next/next/next-script-for-gaPrefer next/script component when using the inline script for Google Analytics.@next/next/no-assign-module-variablePrevent assignment to the module variable.@next/next/no-async-client-componentPrevent client components from being async functions.@next/next/no-before-interactive-script-outside-documentPrevent usage of next/script's beforeInteractive strategy outside of pages/_document.js.@next/next/no-css-tagsPrevent manual stylesheet tags.@next/next/no-document-import-in-pagePrevent importing next/document outside of pages/_document.js.@next/next/no-duplicate-headPrevent duplicate usage of <Head> in pages/_document.js.@next/next/no-head-elementPrevent usage


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 (
    <>

Hello World

      <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 (
    <>

Hello World

      <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>

Hello World

      <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:

I love using Next.js

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 }) =>
{children}
,
    ...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:

This is a list in markdown:

One

Two

Three


Three

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)) //
Hello, Next.js!

}
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)