Moving your Next.js application from Vercel to Cloudflare Pages can provide excellent performance benefits and cost savings. This comprehensive guide will walk you through the entire migration process, including how to resolve common Edge Runtime configuration issues.
Prerequisites
Before starting, ensure you have:
- A Next.js application currently deployed on Vercel
- A Cloudflare account
- A domain name (already configured with Cloudflare)
- Node.js and npm/yarn installed locally
- Git repository access
Understanding Cloudflare Pages Capabilities
First, let's set realistic expectations for what Cloudflare Pages can handle on the free tier:
| Feature | Cloudflare Support | Notes |
|---|---|---|
| Static Pages (SSG) | ✅ Full support | Perfect compatibility |
| Edge Runtime | ✅ Full support | Recommended for dynamic routes |
| Node.js Runtime | ❌ Not supported | Requires code changes |
| API Routes (lightweight) | ✅ Via Workers | Edge runtime only |
| ISR (Incremental Static Regeneration) | ⚠️ Limited | Needs workarounds |
Step 1: Install Cloudflare Adapter
Install the official Cloudflare adapter for Next.js:
npm install -D @cloudflare/next-on-pagesAdd the build script to your package.json:
{
"scripts": {
"build": "next build",
"pages:build": "next build && npx @cloudflare/next-on-pages",
"preview": "npx wrangler pages dev .cloudflare"
}
}Step 2: Configure Next.js for Cloudflare
Update your next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
// Remove or comment out Node-specific configurations
// experimental: {
// runtime: 'nodejs18.x' // This won't work on Cloudflare
// }
};
module.exports = nextConfig;Step 3: Build for Cloudflare Pages
Run the Cloudflare-specific build process:
npm run pages:buildThis command will:
- Run
next buildto create the standard Next.js build - Transform the build output for Cloudflare Pages compatibility
- Generate a
.cloudflaredirectory with the deployable assets
Step 4: Fixing Edge Runtime Configuration Issues
If you encounter the error about routes not configured for Edge Runtime, you need to explicitly declare the runtime for dynamic routes.
Common Error Message
⚡️ ERROR: Failed to produce a Cloudflare Pages build from the project.
⚡️
⚡️ The following routes were not configured to run with the Edge Runtime:
⚡️ - /event-and-contribution
⚡️ - /posts/[file]
Solution: Add Edge Runtime Export
For each problematic route, add the Edge Runtime declaration:
For App Router (app/event-and-contribution/page.tsx):
export const runtime = 'edge';
export default function EventAndContribution() {
// Your component code here
return (
<div>
<h1>Event and Contribution</h1>
{/* Your JSX content */}
</div>
);
}For Dynamic Routes (app/posts/[file]/page.tsx):
export const runtime = 'edge';
interface Props {
params: {
file: string;
};
}
export default function PostPage({ params }: Props) {
return (
<div>
<h1>Post: {params.file}</h1>
{/* Your dynamic content */}
</div>
);
}
// If you're using generateStaticParams
export async function generateStaticParams() {
// Return your static paths
return [
{ file: 'post-1' },
{ file: 'post-2' },
// ... other posts
];
}For Pages Router (pages/posts/[file].tsx):
export const config = {
runtime: 'edge',
};
interface Props {
query: {
file: string;
};
}
export default function PostPage({ query }: Props) {
return (
<div>
<h1>Post: {query.file}</h1>
</div>
);
}Step 5: Test Locally
Before deploying, test your application locally using Wrangler:
npx wrangler pages dev .cloudflareImportant: Always use .cloudflare as the directory, not .next. The .next directory contains Next.js build artifacts that Cloudflare Pages cannot serve directly.
Verify that:
- Static pages load correctly
- Dynamic routes work as expected
- API routes respond properly
- No runtime errors occur
Step 6: Deploy to Cloudflare Pages
Option A: GitHub Integration (Recommended)
- Go to Cloudflare Dashboard → Pages
- Click "Create a project"
- Connect your GitHub repository
- Configure build settings:
- Framework preset: Next.js
- Build command:
npm run pages:build - Build output directory:
.cloudflare
Option B: Direct Upload
For manual deployment or private repositories:
npx wrangler pages deploy .cloudflare --project-name your-project-nameStep 7: Configure Custom Domain
- In Cloudflare Pages, navigate to your project
- Go to Custom domains tab
- Add your domain(s):
example.com www.example.com - Update DNS records as prompted by Cloudflare
Step 8: Update DNS Settings
Configure your DNS records to point to Cloudflare Pages:
Type: CNAME
Name: www
Target: your-project.pages.dev
Proxy: Enabled (orange cloud)
Type: A
Name: @
Target: 192.0.2.1 (Cloudflare will provide the correct IP)
Proxy: Enabled (orange cloud)
Common Migration Issues and Solutions
Issue: Node.js APIs Not Working
Problem: Code using fs, path, or other Node.js modules fails
Solution: Refactor to use Edge-compatible alternatives or move logic to external APIs
Issue: Large Bundle Sizes
Problem: Build fails due to size limits Solution: Optimize imports and use dynamic imports for large dependencies
Issue: Environment Variables
Problem: Environment variables not accessible Solution: Configure environment variables in Cloudflare Pages dashboard under Settings → Environment variables
Free Tier Limitations
Cloudflare Pages free tier includes:
- 500 builds per month
- Unlimited bandwidth (with fair use)
- 100,000 requests per day for Workers
- Global CDN with edge caching
These limits are generous for most applications.
Summary
Migrating from Vercel to Cloudflare Pages requires:
- Installing the Cloudflare adapter
- Configuring Edge Runtime for dynamic routes
- Building with the Cloudflare-specific command
- Testing locally before deployment
- Deploying via GitHub integration or direct upload
- Configuring custom domains and DNS
The key insight is that Cloudflare Pages requires explicit Edge Runtime declaration for any dynamic routes, while Vercel allows Node.js runtime by default. Once you configure your routes properly with export const runtime = 'edge', your Next.js application should work seamlessly on Cloudflare Pages.
Pro tip: Start with a staging deployment to verify everything works before switching your production DNS. This approach ensures zero downtime and allows easy rollback if needed.
