Cloudflare Docs
Pages
Edit this page on GitHub
Set theme to dark (⇧+D)

Deploy a Next.js site

Next.js is an open-source React framework for creating websites and applications. In this guide, you will create a new Next.js application and deploy it using Cloudflare Pages.

This guide will instruct you how to deploy a full-stack Next.js project which uses the Edge Runtime, via the next-on-pages adapter.

​​ Create a new project using the create-cloudflare CLI (C3)

The create-cloudflare CLI (C3) will configure your Next.js site for Cloudflare Pages. Run the following command in your terminal to create a new Next.js site:

$ npm create cloudflare@latest my-next-app -- --framework=next

C3 will ask you a series of setup questions. C3 will also install necessary dependencies, including the Wrangler CLI and the @cloudflare/next-on-pages adapter.

After creating your project, C3 will generate a new my-next-app directory using the default Next.js template, updated to be fully compatible with Cloudflare Pages.

When creating your new project, C3 will give you the option of deploying an initial version of your application via Direct Upload. You can redeploy your application at any time by running following command inside your project directory:

$ npm run deploy

​​ Configure and deploy a project without C3

If you already have a Next.js project or wish to manually create and deploy one without using C3, Cloudflare recommends that you use @cloudflare/next-on-pages and refer to its README for instructions and additional information to help you develop and deploy your project.

​​ Git integration

In addition to Direct Upload deployments, you can deploy projects via Git integration. Git integration allows you to connect a GitHub or GitLab repository to your Pages application and have your Pages application automatically built and deployed after each new commit is pushed to it.

Setup requires a basic understanding of Git. If you are new to Git, refer to GitHub’s summarized Git handbook on how to set up Git on your local machine.

​​ Create a new GitHub repository

Create a new GitHub repository by visiting repo.new. After creating a new repository, go to your newly created project directory to prepare and push your local application to GitHub by running the following commands in your terminal:
# Skip the following three commands if you have built your application
# using C3 or already committed your changes
$ git init
$ git add .
$ git commit -m "Initial commit"
$ git branch -M main
$ git remote add origin https://github.com/<your-gh-username>/<repository-name>
$ git push -u origin main

​​ Connect your application to the GitHub repository via the Cloudflare dashboard

  1. Log in to the Cloudflare dashboard and select your account.
  2. In Account Home, select Workers & Pages > Create application > Pages > Connect to Git.

You will be asked to authorize access to your GitHub account if you have not already done so. Cloudflare needs this so that it can monitor and deploy your projects from the source. You may narrow access to specific repositories if you prefer. However, you will have to manually update this list within your GitHub settings when you want to add more repositories to Cloudflare Pages.

  1. Select the new GitHub repository that you created and, in the Set up builds and deployments section, provide the following information:
Configuration optionValue
Production branchmain
Build commandnpx @cloudflare/next-on-pages@1
Build directory.vercel/output/static

Optionally, you can customize the Project name field. It defaults to the GitHub repository’s name, but it does not need to match. The Project name value is assigned as your *.pages.dev subdomain.

  1. After completing configuration, select Save and Deploy.

You will be able to review your first deploy pipeline in progress. Pages installs all dependencies and builds the project as specified. Cloudflare Pages will automatically rebuild your project and deploy it on every new pushed commit.

Additionally, you will have access to preview deployments, which repeat the build-and-deploy process for pull requests. With these, you can preview changes to your project with a real URL before deploying your changes to production.

​​ Use bindings in your Next.js application

A binding allows your application to interact with Cloudflare developer products, such as KV, Durable Objects, R2, and D1.

If you intend to use bindings in your project, you must first set up your bindings for local and remote development.

​​ Set up bindings for local development

To set up bindings for use in local development, you will use the setupDevPlatform function provided by @cloudflare/next-on-pages/next-dev. setupDevPlatform sets up a platform emulation based on your project’s wrangler.toml file that your Next.js application can make use of locally.

For example, to work with a KV binding locally, open the Next.js configuration file and add:

next.config.mjs
import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev'
// note: the if statement is present because you
// only need to use the function during development
if (process.env.NODE_ENV === 'development') {
await setupDevPlatform()
}
/** @type {import('next').NextConfig} */
const nextConfig = {}
export default nextConfig
next.config.js / next.config.cjs
// note: the if statement is present because you
// only need to use the function during development
if (process.env.NODE_ENV === "development") {
const { setupDevPlatform } = require("@cloudflare/next-on-pages/next-dev")
setupDevPlatform()
}
/** @type {import('next').NextConfig} */
const nextConfig = {}
module.exports = nextConfig

Make sure to have a wrangler.toml file at the root of your project with a declaration for a KV binding named MY_KV:

wrangler.toml
name = "my-next-app"
compatibility_flags = ["nodejs_compat"]
[[kv_namespaces]]
binding = "MY_KV"
id = "<YOUR_KV_NAMESPACE_ID>"

​​ Set up bindings for a deployed application

To access bindings in a deployed application, you will need to configure any necessary bindings and connect them to your project via your project’s settings page in the Cloudflare dashboard.

​​ Add bindings to Typescript projects

If your project is using TypeScript, you will want to set up proper type support so you can access your bindings in a type-safe and convenient manner.

To get proper type support, you need to create a new env.d.ts file in your project and extend the CloudflareEnv (used by getRequestContext) interface with your bindings.

The following is an example of how to add a KVNamespace binding:

env.d.ts
interface CloudflareEnv {
// The KV Namespace binding type used here comes
// from `@cloudflare/workers-types`. To use it in such
// a way make sure that you have installed the package
// as a dev dependency and you have added it to your
//`tsconfig.json` file under `compilerOptions.types`.
MY_KV: KVNamespace
}

​​ Access bindings in the application

Local and remote bindings can be accessed using the getRequestContext function exposed by @cloudflare/next-on-pages. The following code example shows how to access them in a hello API route of an App Router application.

app/api/hello/route.js
import { getRequestContext } from '@cloudflare/next-on-pages'
export const runtime = 'edge'
// ...
export async function GET(request) {
// this is the KV binding you defined in the wrangler.toml file
const myKv = getRequestContext().env.MY_KV
// get a value from the namespace
const kvValue = await myKv.get(`kvTest`) || false
return new Response(`The value of kvTest in MY_KV is: ${kvValue}`)
}
app/api/hello/route.ts
import type { NextRequest } from 'next/server'
import { getRequestContext } from '@cloudflare/next-on-pages'
export const runtime = 'edge'
// ...
export async function GET(request: NextRequest) {
// this is the KV binding you defined in the wrangler.toml file
const myKv = getRequestContext().env.MY_KV
// get a value from the namespace
const kvValue = await myKv.get(`kvTest`) || false
return new Response(`The value of kvTest in MY_KV is: ${kvValue}`)
}

​​ Image component

The Cloudflare network does not provide the same image optimization support as the Vercel network does. Because of this, the Next.js’ <Image /> component behaves differently from how it would in the Vercel network.

  • If you build your application as a static site, the <Image /> component will not serve any images.

  • If you build your application using @cloudflare/next-on-pages, the component will work but it will not perform any image optimization (regardless of the props you pass to it).

Both cases can be improved by setting up proper loaders for the <Image /> component, which allow you to use any image optimization service you want. To use Cloudflare Images, refer to resize with Cloudflare Workers.

When developing a next-on-pages application, this is the development workflow that Cloudflare recommends:

​​ Develop using the standard Next.js dev server

The standard development server provided by Next.js is the best available option for a fast and polished development experience. The next-dev submodule (as described in the local bindings section) makes it possible to use Next.js’ standard development server while still having access to your Cloudflare bindings.

​​ Build and preview your application locally

To ensure that your application is being built in a manner that is fully compatible with Cloudflare Pages, before deploying it, or whenever you are comfortable checking the correctness of the application during your development process, you will want to build and preview it locally using Cloudflare’s workerd JavaScript runtime.

If you have created your project with C3, do this by running:

$ npm run preview

If you have created your project without C3, run:

$ npx @cloudflare/next-on-pages@1

And preview your project by running:

$ npx wrangler pages dev .vercel/output/static

​​ Deploy your application and iterate

After you have previewed your application locally, you can deploy it to Cloudflare Pages (both via Direct Uploads or Git integration) and iterate over the process to make new changes.

​​ Troubleshooting

Review common mistakes and issues that you might encounter when developing a Next.js application using next-on-pages.

​​ Edge runtime

All server-side routes in your Next.js project must be configured as Edge runtime routes when running on Cloudflare Pages. You must add export const runtime = 'edge' to each individual server-side route.

​​ App router

​​ Not found

Next.js generates a not-found route for your application under the hood during the build process. In some circumstances, Next.js can detect that the route requires server-side logic (particularly if computation is being performed in the root layout component) and Next.js might create a Node.js serverless function (which, as such, is incompatible with @cloudflare/next-on-pages).

To prevent this incompatibility, Cloudflare recommends to always provide a custom not-found route which explicitly opts in the edge runtime:

(src/)app/not-found.(jsx|tsx)
export const runtime = 'edge'
export default async function NotFound() {
// ...
return (
// ...
)
}

​​ generateStaticParams

When doing static site generation (SSG) in the /app directory and using the generateStaticParams function, Next.js by default tries to handle requests for non statically generated routes on-demand. It does so by creating a Node.js serverless function (which, as such, is incompatible with @cloudflare/next-on-pages).

In such cases you need to instruct Next.js not to do so by specifying a false dynamicParams:

app/my-example-page/[slug]/page.jsx
+ export const dynamicParams = false
// ...

​​ Top-level getRequestContext

The getRequestContext function cannot be called at the top level of a route file or in any manner that triggers the function call as part of the file’s initialization.

getRequestContext must be called inside the request handling process logic and not in a global/unconditional manner that gets triggered as soon as the file is imported.

For example, the following is an incorrect usage of getRequestContext:

app/api/myvar/route.js
import { getRequestContext } from '@cloudflare/next-on-pages'
export const runtime = 'edge'
const myVariable = getRequestContext().env.MY_VARIABLE
export async function GET(request) {
return new Response(myVariable)
}

The above example can be fixed in the following way:

app/api/myvar/route.js
import { getRequestContext } from '@cloudflare/next-on-pages'
export const runtime = 'edge'
export async function GET(request) {
const myVariable = getRequestContext().env.MY_VARIABLE
return new Response(myVariable)
}

​​ Pages router

​​ getStaticPaths

When doing static site generation (SSG) in the /pages directory and using the getStaticPaths function, Next.js by default tries to handle requests for non statically generated routes on-demand. It does so by creating a Node.js serverless function (which, as such, is incompatible with @cloudflare/next-on-pages).

In such cases, you need to instruct Next.js not to do so by specifying a false fallback:

pages/my-example-page/[slug].jsx
// ...
export async function getStaticPaths() {
// ...
return {
paths,
+ fallback: false,
}
}

​​ Learn more

By completing this guide, you have successfully deployed your Next.js site to Cloudflare Pages. To get started with other frameworks, refer to the list of Framework guides.