Reference:
-
npx create-next-app@latest my-app
Note that if you elect to use eslint, there are a couple of places you need to add return types to make the default template pass the pre-build checks.
-
cd
into the new directory (e.g.cd my-app
) -
npm install -D @cloudflare/next-on-pages vercel
-
Configure the project to use the Edge Runtime:
-
Replace
pages/api/hello.ts
with the following:// Next.js Edge API Routes: https://nextjs.org/docs/api-routes/edge-api-routes import type { NextRequest } from "next/server"; export const config = { runtime: "experimental-edge", }; export default async function handler( req: NextRequest ): Promise<Response> { return new Response(JSON.stringify({ name: "John Doe" }), { status: 200, headers: { "Content-Type": "application/json", }, }); }
-
Add the following to
next.config.js
:/** @type {import('next').NextConfig} */ const nextConfig = { + experimental: { + runtime: "experimental-edge", + }, reactStrictMode: true, swcMinify: true, }; module.exports = nextConfig;
-
-
git commit
andgit push
to a GitHub/GitLab repository. -
Create a Pages project, connect that repository, and select "Next.js" from the framework preset list.
Option Value Build command npx @cloudflare/next-on-pages --experimental-minify
Build output directory .vercel/output/static
-
Add a
NODE_VERSION
environment variable set to14
or greater. -
In the Pages project Settings > Functions > Compatibility Flags, add the
transformstream_enable_standard_constructor
andstreams_enable_constructors
flags. These will not be necessary once they graduate to be on by default on 2022-11-30's compatibility date. -
The project should now be ready to deploy. Create a new deployment.
⚡️ @cloudflare/next-to-pages CLI
⚡️
⚡️ Usage: npx @cloudflare/next-to-pages [options]
⚡️
⚡️ Options:
⚡️
⚡️ --help: Shows this help message
⚡️
⚡️ --skip-build: Doesn't run 'vercel build' automatically
⚡️
⚡️ --experimental-minify: Attempts to minify the functions of a project (by de-duping webpack chunks)
⚡️
⚡️ --watch: Automatically rebuilds when the project is edited
⚡️
⚡️
⚡️ GitHub: https://github.com/cloudflare/next-on-pages
⚡️ Docs: https://developers.cloudflare.com/pages/framework-guides/deploy-a-nextjs-site/
Local testing
In one terminal, run npx @cloudflare/next-on-pages --watch
, and in another npx wrangler pages dev .vercel/output/static --compatibility-flags=streams_enable_constructors
. We hope to soon make improvements to the refresh speed.
config.json property |
Support |
---|---|
version | 3 |
routes src |
✅ |
routes dest |
🔄 |
routes headers |
🔄 |
routes methods |
✅ |
routes continue |
🔄 |
routes caseSensitive |
✅ |
routes check |
🔄 |
routes status |
🔄 |
routes has |
✅ |
routes missing |
✅ |
routes locale |
🔄 |
routes middlewarePath |
✅ |
images | ❌ (see Cloudflare's Image Resizing documentation) |
wildcard | 🔄 |
overrides | 🔄 |
cache | ❌ |
- ✅: Supported
- 🔄: Not currently supported, but it's probably possible and we may add support in the future
- ❌: Not supported and unlikely we ever will support this
Add the following to next.config.js
:
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
runtime: "experimental-edge",
+ appDir: true,
},
reactStrictMode: true,
swcMinify: true,
};
module.exports = nextConfig;
If you're following the Next.js 12 → 13 Upgrade Guide, delete any ./pages/_app.tsx
and ./pages/index.tsx
files and replace with ./app/layout.tsx
and ./app/page.tsx
:
// ./app/layout.tsx
import "../styles/globals.css";
import { FC } from "react";
const RootLayout: FC<{
children: React.ReactNode;
}> = ({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}) => {
return (
<html lang="en">
<body>{children}</body>
</html>
);
};
export default RootLayout;
// ./app/page.tsx
import { FC } from "react";
import styles from "../styles/Home.module.css";
const Home = async (): Promise<ReturnType<FC>> => {
const uuid = await fetch("https://uuid.rocks/plain").then(
async (response) => await response.text()
);
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>
<p className={styles.description}>
Get started by editing{" "}
<code className={styles.code}>pages/index.tsx</code>
</p>
<p className={styles.description}>
Here's a server-side UUID:
<code className={styles.code}>{uuid}</code>
</p>
</main>
</div>
);
};
export default Home;
// ./pages/api/some_route.ts
import type { NextRequest } from "next/server";
export const config = {
runtime: "experimental-edge",
};
export default async function handler(req: NextRequest) {
return new Response(
JSON.stringify({
name: process.env.NEXT_RUNTIME,
}),
{
status: 200,
headers: {
"content-type": "application/json",
},
}
);
}
Server-side rendering (SSR) pages with getServerSideProps()
// ./pages/ssr.tsx
import type { NextPage } from "next";
import Head from "next/head";
import styles from "../styles/Home.module.css";
export const config = {
runtime: "experimental-edge",
};
export const getServerSideProps = async () => {
return {
props: {
runtime: process.env.NEXT_RUNTIME,
uuid: await fetch("https://uuid.rocks/plain").then((response) =>
response.text()
),
},
};
};
const Home: NextPage<{ runtime: string; uuid: string }> = ({
runtime,
uuid,
}) => {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to{" "}
<a href="https://nextjs.org">Next.js, running at the {runtime}!</a>
</h1>
<p className={styles.description}>
Get started by editing{" "}
<code className={styles.code}>pages/index.tsx</code>
</p>
<p className={styles.description}>
Here's a server-side UUID:
<code className={styles.code}>{uuid}</code>
</p>
</main>
</div>
);
};
export default Home;
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL("/about-2", request.url));
}
export const config = {
matcher: "/about/:path*",
};