gavmac00/nextjs-app-docs huggingface dataset:
throw new Error('Failed to fetch data')
  }
 
  return res.json()
}
 
export default async function Page() {
  const data = await getData()
 
  return <main></main>
}
Good to know:
To use an async Server Component with TypeScript, ensure you are using TypeScript 5.1.3 or higher and @types/react 18.2.8 or higher.
Server Component Functions
Next.js provides helpful server functions you may need when fetching data in Server Components:
cookies()
headers()
use in Client Components
use is a new React function that accepts a promise conceptually similar to await. use handles the promise returned by a function in a way that is compatible with components, hooks, and Suspense. Learn more about use in the React RFC.
Wrapping fetch in use is currently not recommended in Client Components and may trigger multiple re-renders. For now, if you need to fetch data in a Client Component, we recommend using a third-party library such as SWR or React Query.
Good to know: We'll be adding more examples once fetch and use work in Client Components.
Static Data Fetching
By default, fetch will automatically fetch and cache data indefinitely.
 fetch('https://...') // cache: 'force-cache' is the default
Revalidating Data
To revalidate cached data at a timed interval, you can use the next.revalidate option in fetch() to set the cache lifetime of a resource (in seconds).
 fetch('https://...', { next: { revalidate: 10 } })
See Revalidating Data for more information.
Good to know:
See Revalidating Data for more information.
Good to know:
Caching at the fetch level with revalidate or cache: 'force-cache' stores the data across requests in a shared cache. You should avoid using it for user-specific data (i.e. requests that derive data from cookies() or headers())
Dynamic Data Fetching
To fetch fresh data on every fetch request, use the cache: 'no-store' option.
 fetch('https://...', { cache: 'no-store' })
Data Fetching Patterns
Parallel Data Fetching
To minimize client-server waterfalls, we recommend this pattern to fetch data in parallel:
app/artist/[username]/page.tsx import Albums from './albums'
 
async function getArtist(username: string) {
  const res = await fetch(`https://api.example.com/artist/${username}`)
  return res.json()
}
return res.json()
}
 
async function getArtistAlbums(username: string) {
  const res = await fetch(`https://api.example.com/artist/${username}/albums`)
  return res.json()
}
 
export default async function Page({
  params: { username },
}: {
  params: { username: string }
}) {
  // Initiate both requests in parallel
  const artistData = getArtist(username)
  const albumsData = getArtistAlbums(username)
 
  // Wait for the promises to resolve
  const [artist, albums] = await Promise.all([artistData, albumsData])
 
  return (
    <>
      <h1>{artist.name}</h1>
      <Albums list={albums}></Albums>
    </>
  )
}
</>
  )
}
By starting the fetch prior to calling await in the Server Component, each request can eagerly start to fetch requests at the same time. This sets the components up so you can avoid waterfalls.
We can save time by initiating both requests in parallel, however, the user won't see the rendered result until both promises are resolved.
To improve the user experience, you can add a suspense boundary to break up the rendering work and show part of the result as soon as possible:
artist/[username]/page.tsx import { getArtist, getArtistAlbums, type Album } from './api'
 
export default async function Page({
  params: { username },
}: {
  params: { username: string }
}) {
  // Initiate both requests in parallel
  const artistData = getArtist(username)
  const albumData = getArtistAlbums(username)
const albumData = getArtistAlbums(username)
 
  // Wait for the artist's promise to resolve first
  const artist = await artistData
 
  return (
    <>
      <h1>{artist.name}</h1>
      {/* Send the artist information first,
          and wrap albums in a suspense boundary */}
      <Suspense fallback={<div>Loading...</div>}>
        <Albums promise={albumData} />
      </Suspense>
    </>
  )
}
 
// Albums Component
async function Albums({ promise }: { promise: Promise<Album[]> }) {
  // Wait for the albums promise to resolve
  const albums = await promise
 
  return (
    <ul>
      {albums.map((album) => (
        <li key={album.id}>{album.name}</li>
      ))}
    </ul>
  )
))}
    </ul>
  )
}
Take a look at the preloading pattern for more information on improving components structure.
Sequential Data Fetching
To fetch data sequentially, you can fetch directly inside the component that needs it, or you can await the result of fetch inside the component that needs it:
app/artist/page.tsx // ...
 
async function Playlists({ artistID }: { artistID: string }) {
  // Wait for the playlists
  const playlists = await getArtistPlaylists(artistID)
 
  return (
    <ul>
      {playlists.map((playlist) => (
        <li key={playlist.id}>{playlist.name}</li>
      ))}
    </ul>
  )
}
 
export default async function Page({
  params: { username },
}: {
  params: { username: string }
}) {
}: {
  params: { username: string }
}) {
  // Wait for the artist
  const artist = await getArtist(username)
 
  return (
    <>
      <h1>{artist.name}</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <Playlists artistID={artist.id} />
      </Suspense>
    </>
  )
}
By fetching data inside the component, each fetch request and nested segment in the route cannot start fetching data and rendering until the previous request or segment has completed.
Blocking Rendering in a Route
By fetching data in a layout, rendering for all route segments beneath it can only start once the data has finished loading.
In the pages directory, pages using server-rendering would show the browser loading spinner until getServerSideProps had finished, then render the React component for that page. This can be described as "all or nothing" data fetching. Either you had the entire data for your page, or none.
In the app directory, you have additional options to explore:
First, you can use loading.js to show an instant loading state from the server while streaming in the result from your data fetching function.
Second, you can move data fetching lower in the component tree to only block rendering for the parts of the page that need it. For example, moving data fetching to a specific component rather than fetching it at the root layout.
Whenever possible, it's best to fetch data in the segment that uses it. This also allows you to show a loading state for only the part of the page that is loading, and not the entire page.
Data Fetching without fetch()
Data Fetching without fetch()
You might not always have the ability to use and configure fetch requests directly if you're using a third-party library such as an ORM or database client.
In cases where you cannot use fetch but still want to control the caching or revalidating behavior of a layout or page, you can rely on the default caching behavior of the segment or use the segment cache configuration.
Default Caching Behavior
Any data fetching libraries that do not use fetch directly will not affect caching of a route, and will be static or dynamic depending on the route segment.
If the segment is static (default), the output of the request will be cached and revalidated (if configured) alongside the rest of the segment. If the segment is dynamic, the output of the request will not be cached and will be re-fetched on every request when the segment is rendered.
Good to know: Dynamic functions like cookies() and headers() will make the route segment dynamic.
Good to know: Dynamic functions like cookies() and headers() will make the route segment dynamic.
Segment Cache Configuration
As a temporary solution, until the caching behavior of third-party queries can be configured, you can use segment configuration to customize the cache behavior of the entire segment.
app/page.tsx import prisma from './lib/prisma'
 
export const revalidate = 3600 // revalidate every hour
 
async function getPosts() {
  const posts = await prisma.post.findMany()
  return posts
}
 
export default async function Page() {
  const posts = await getPosts()
  // ...
}
Revalidating DataNext.js allows you to update specific static routes without needing to rebuild your entire site. Revalidation (also known as Incremental Static Regeneration) allows you to retain the benefits of static while scaling to millions of pages.
There are two types of revalidation in Next.js:
Background: Revalidates the data at a specific time interval.
On-demand: Revalidates the data based on an event such as an update.
Background Revalidation
To revalidate cached data at a specific interval, you can use the next.revalidate option in fetch() to set the cache lifetime of a resource (in seconds).
 fetch('https://...', { next: { revalidate: 60 } })
If you want to revalidate data that does not use fetch (i.e. using an external package or query builder), you can use the route segment config.
app/page.tsx export const revalidate = 60 // revalidate this page every 60 seconds
In addition to fetch, you can also revalidate data using cache.
How it works
When a request is made to the route that was statically rendered at build time, it will initially show the cached data.
Any requests to the route after the initial request and before 60 seconds are also cached and instantaneous.
After the 60-second window, the next request will still show the cached (stale) data.
Next.js will trigger a regeneration of the data in the background.
Once the route generates successfully, Next.js will invalidate the cache and show the updated route. If the background regeneration fails, the old data would still be unaltered.
When a request is made to a route segment that hasn’t been generated, Next.js will dynamically render the route on the first request. Future requests will serve the static route segments from the cache.
Good to know: Check if your upstream data provider has caching enabled by default. You might need to disable (e.g. useCdn: false), otherwise a revalidation won't be able to pull fresh data to update the ISR cache. Caching can occur at a CDN (for an endpoint being requested) when it returns the Cache-Control header. ISR on Vercel persists the cache globally and handles rollbacks.
On-demand Revalidation
If you set a revalidate time of 60, all visitors will see the same generated version of your site for one minute. The only way to invalidate the cache is if someone visits the page after the minute has passed.
The Next.js App Router supports revalidating content on-demand based on a route or cache tag. This allows you to manually purge the Next.js cache for specific fetches, making it easier to update your site when:
Content from your headless CMS is created or updated.
Content from your headless CMS is created or updated.
Ecommerce metadata changes (price, description, category, reviews, etc).
Using On-Demand Revalidation
Data can be revalidated on-demand by path (revalidatePath) or by cache tag (revalidateTag).
For example, the following fetch adds the cache tag collection:
app/page.tsx export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}
This cached data can then be revalidated on-demand by calling revalidateTag in a Route Handler.
app/api/revalidate/route.ts import { NextRequest, NextResponse } from 'next/server'
import { revalidateTag } from 'next/cache'
 
export async function GET(request: NextRequest) {
export async function GET(request: NextRequest) {
  const tag = request.nextUrl.searchParams.get('tag')
  revalidateTag(tag)
  return NextResponse.json({ revalidated: true, now: Date.now() })
}
Error Handling and Revalidation
If an error is thrown while attempting to revalidate data, the last successfully generated data will continue to be served from the cache. On the next subsequent request, Next.js will retry revalidating the data.
Caching DataNext.js has built-in support for caching data, both on a per-request basis (recommended) or for an entire route segment.
Per-request Caching
fetch()
By default, all fetch() requests are cached and deduplicated automatically. This means that if you make the same request twice, the second request will reuse the result from the first request.
app/page.tsx async function getComments() {
  const res = await fetch('https://...') // The result is cached
  return res.json()
}
 
// This function is called twice, but the result is only fetched once
const comments = await getComments() // cache MISS
 
// The second call could be anywhere in your application
const comments = await getComments() // cache HIT
Requests are not cached if:
const comments = await getComments() // cache HIT
Requests are not cached if:
Dynamic methods (next/headers, export const POST, or similar) are used and the fetch is a POST request (or uses Authorization or cookie headers)
fetchCache is configured to skip cache by default
revalidate: 0 or cache: 'no-store' is configured on individual fetch
Requests made using fetch can specify a revalidate option to control the revalidation frequency of the request.
app/page.tsx export default async function Page() {
  // revalidate this data every 10 seconds at most
  const res = await fetch('https://...', { next: { revalidate: 10 } })
  const data = res.json()
  // ...
}
React cache()
const data = res.json()
  // ...
}
React cache()
React allows you to cache() and deduplicate requests, memoizing the result of the wrapped function call. The same function called with the same arguments will reuse a cached value instead of re-running the function.
utils/getUser.ts import { cache } from 'react'
 
export const getUser = cache(async (id: string) => {
  const user = await db.user.findUnique({ id })
  return user
})
app/user/[id]/layout.tsx import { getUser } from '@utils/getUser'
 
export default async function UserLayout({
  params: { id },
}: {
  params: { id: string }
}) {
  const user = await getUser(id)
  // ...
}
app/user/[id]/page.tsx import { getUser } from '@utils/getUser'
export default async function Page({
  params: { id },
}: {
  params: { id: string }
}) {
  const user = await getUser(id)
  // ...
}
Although the getUser() function is called twice in the example above, only one query will be made to the database. This is because getUser() is wrapped in cache(), so the second request can reuse the result from the first request.
Good to know:
fetch() caches requests automatically, so you don't need to wrap functions that use fetch() with cache(). See automatic request deduping for more information.
In this new model, we recommend fetching data directly in the component that needs it, even if you're requesting the same data in multiple components, rather than passing the data between components as props.
We recommend using the server-only package to make sure server data fetching functions are never used on the client.
POST requests and cache()
POST requests and cache()
POST requests are automatically deduplicated when using fetch – unless they are inside of POST Route Handler or come after reading headers()/cookies(). For example, if you are using GraphQL and POST requests in the above cases, you can use cache to deduplicate requests. The cache arguments must be flat and only include primitives. Deep objects won't match for deduplication.
utils/getUser.ts import { cache } from 'react'
 
export const getUser = cache(async (id: string) => {
  const res = await fetch('...', { method: 'POST', body: '...' })
  // ...
})
Preload pattern with cache()
As a pattern, we suggest optionally exposing a preload() export in utilities or components that do data fetching.
components/User.tsx import { getUser } from '@utils/getUser'
 
export const preload = (id: string) => {
export const preload = (id: string) => {
  // void evaluates the given expression and returns undefined
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
  void getUser(id)
}
export default async function User({ id }: { id: string }) {
  const result = await getUser(id)
  // ...
}
By calling preload, you can eagerly start fetching data you're likely going to need.
app/user/[id]/page.tsx import User, { preload } from '@components/User'
 
export default async function Page({
  params: { id },
}: {
  params: { id: string }
}) {
  preload(id) // starting loading the user data now
  const condition = await fetchCondition()
  return condition ? <User id={id} /> : null
}
Good to know:
}
Good to know:
The preload() function can have any name. It's a pattern, not an API.
This pattern is completely optional and something you can use to optimize on a case-by-case basis.
This pattern is a further optimization on top of parallel data fetching. Now you don't have to pass promises down as props and can instead rely on the preload pattern.
Combining cache, preload, and server-only
You can combine the cache function, the preload pattern, and the server-only package to create a data fetching utility that can be used throughout your app.
utils/getUser.ts import { cache } from 'react'
import 'server-only'
 
export const preload = (id: string) => {
  void getUser(id)
}
 
export const getUser = cache(async (id: string) => {
  // ...
})
export const getUser = cache(async (id: string) => {
  // ...
})
With this approach, you can eagerly fetch data, cache responses, and guarantee that this data fetching only happens on the server.
The getUser.ts exports can be used by layouts, pages, or components to give them control over when a user's data is fetched.
Segment-level Caching
Good to know: We recommend using per-request caching for improved granularity and control over caching.
Segment-level caching allows you to cache and revalidate data used in route segments.
This mechanism allows different segments of a path to control the cache lifetime of the entire route. Each page.tsx and layout.tsx in the route hierarchy can export a revalidate value that sets the revalidation time for the route.
app/page.tsx export const revalidate = 60 // revalidate this segment every 60 seconds
Good to know:
Good to know:
If a page, layout, and fetch request inside components all specify a revalidate frequency, the lowest value of the three will be used.
Advanced: You can set fetchCache to 'only-cache' or 'force-cache' to ensure that all fetch requests opt into caching but the revalidation frequency might still be lowered by individual fetch requests. See fetchCache for more information.
CSS Modules
Next.js has built-in support for CSS Modules using the .module.css extension.
CSS Modules locally scope CSS by automatically creating a unique class name. This allows you to use the same class name in different files without worrying about collisions. This behavior makes CSS Modules the ideal way to include component-level CSS.
Example
CSS Modules can be imported into any file inside the app directory:app/dashboard/layout.tsx import styles from './styles.module.css'
 
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section className={styles.dashboard}>{children}</section>
}app/dashboard/styles.module.css .dashboard {
  padding: 24px;
}
CSS Modules are an optional feature and are only enabled for files with the .module.css extension.
Regular <link> stylesheets and global CSS files are still supported.
Regular <link> stylesheets and global CSS files are still supported.
In production, all CSS Module files will be automatically concatenated into many minified and code-split .css files.
These .css files represent hot execution paths in your application, ensuring the minimal amount of CSS is loaded for your application to paint.
Global Styles
Global styles can be imported into any layout, page, or component inside the app directory.
Good to know: This is different from the pages directory, where you can only import global styles inside the _app.js file.
For example, consider a stylesheet named app/global.css: body {
  padding: 20px 20px 60px;
  max-width: 680px;
  margin: 0 auto;
}Inside the root layout (app/layout.js), import the global.css stylesheet to apply the styles to every route in your application:app/layout.tsx // These styles apply to every route in the application
import './global.css'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
External Stylesheets
Stylesheets published by external packages can be imported anywhere in the app directory, including colocated components:app/layout.tsx import 'bootstrap/dist/css/bootstrap.css'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className="container">{children}</body>
    </html>
  )
}
Good to know: External stylesheets must be directly imported from an npm package or downloaded and colocated with your codebase. You cannot use <link rel="stylesheet" />.
Additional Features
Additional Features
Next.js includes additional features to improve the authoring experience of adding styles:
When running locally with next dev, local stylesheets (either global or CSS modules) will take advantage of Fast Refresh to instantly reflect changes as edits are saved.
When building for production with next build, CSS files will be bundled into fewer minified .css files to reduce the number of network requests needed to retrieve styles.
If you disable JavaScript, styles will still be loaded in the production build (next start). However, JavaScript is still required for next dev to enable Fast Refresh.
Tailwind CSS
Tailwind CSS is a utility-first CSS framework that works exceptionally well with Next.js.
Installing Tailwind
Install the Tailwind CSS packages and run the init command to generate both the tailwind.config.js and postcss.config.js files:
Terminal npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Configuring Tailwind
Inside tailwind.config.js, add paths to the files that will use Tailwind CSS class names:
tailwind.config.js /** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}', // Note the addition of the `app` directory.
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
 
    // Or if using `src` directory:
    './src/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
You do not need to modify postcss.config.js.
Importing Styles
Add the Tailwind CSS directives that Tailwind will use to inject its generated styles to a Global Stylesheet in your application, for example:app/globals.css @tailwind base;
@tailwind components;
@tailwind utilities;Inside the root layout (app/layout.tsx), import the globals.css stylesheet to apply the styles to every route in your application.app/layout.tsx import type { Metadata } from 'next'
 
// These styles apply to every route in the application
import './globals.css'
// These styles apply to every route in the application
import './globals.css'
 
export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}Using Classes
After installing Tailwind CSS and adding the global styles, you can use Tailwind's utility classes in your application.app/page.tsx export default function Page() {
  return <h1 className="text-3xl font-bold underline">Hello, Next.js!</h1>
}
Usage with Turbopack
As of Next.js 13.1, Tailwind CSS and PostCSS are supported with Turbopack.