Get Vercel-level deployment without Vercel-level prices


Without a doubt, Vercel is the ideal platform for deploying a Next.js app. This makes sense, since Vercel created Next.js. And while it offers a generous free plan, things can get expensive quickly as your app grows. Depending on your situation, Vercel may end up being less cost-effective compared to other options.

Vercel-level deployment without Vercel-level prices

This tutorial will explore how to build and deploy a Next.js app to Cloudflare Workers. We’ll also cover how to configure GitHub integration for CI/CD and optimize images using Cloudflare Images. With the final setup, you can enjoy Vercel-like performance with more flexibility and lower costs, especially at scale.

To follow along with the tutorial, you’ll need a Cloudflare account. Sign up here if you don’t have one yet.

How to deploy a Next.js app to Cloudflare

For this tutorial, we’ll create a simple product display application that uses a Next.js API route to fetch products from the Fake Store API. Then, on the homepage, we’ll make a request to that API route and display the products in a list.

Create a Next.js app with OpenNext

To get started, run the command below to scaffold a new app using the OpenNext adapter:

npm create cloudflare@latest -- my-next-app --framework=next --platform=workers

This command sets up a Next.js project configured to run on Cloudflare Workers. During setup, you’ll be asked to customize the project like a typical create-next-app flow. However, once the project is created, the following new files will be added by OpenNext:

  • Wrangler.json – This file includes configurations for how Cloudflare Workers should deploy your app, such as the deployment name, environment variables, build settings, and the location of your build output
  • Opennext.config.mjs – Handles how OpenNext builds and serves your app

To proceed, navigate to pages/api/ directory, create a new file called store.js, and paste the following code into it:

export default async function handler(req, res) {
  try {
    const response = await fetch("https://fakestoreapi.com/products/");
    const products = await response.json();
    res.status(200).json(products);
  } catch (error) {
    res.status(500).json({ error: "Failed to fetch products" });
  }
}

The code above simply creates an API route that returns product data from fakestoreapi.com.

Next, open the default pages/index.js file and replace its contents with the following code:

import { useState, useEffect } from "react";
import { Geist } from "next/font/google";
import Image from "next/image";

const geist = Geist({
  subsets: ["latin"],
  variable: "--font-geist",
});

export default function Home() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch("/api/store")
      .then((res) => res.json())
      .then((data) => {
        setProducts(data);
        setLoading(false);
      });
  }, []);

  if (loading) {
    return (
      

Loading...

); } return (
{products.map((product) => (
${product.price}

{product.rating.rate} ({product.rating.count})

{product.title}

{product.category}

))}
); }

This code updates the homepage to fetch and display a list of products using the API we just created. Also, since we’re loading images from the Fake Store API, we need to allow that external domain in the images config of Next.js. To do this, update your next.config.mjs file to match the one below:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ["fakestoreapi.com"],
  },
};

export default nextConfig;

// added by create cloudflare to enable calling `getCloudflareContext()` in `next dev`
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
initOpenNextCloudflareForDev();

Now, run the app locally:

npm run dev

You should see a list of fake products on your homepage, as shown in the image below:fake store nextjs app created with opennext

Deploy to Cloudflare

As mentioned earlier, the wrangler.json file lets you customize your app’s deployment settings. You can easily change the app name before deployment by updating the name field. In my case, I’m changing the name to storeapp, as shown below:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "storeapp", // Update this to the name of your project
  "main": ".open-next/worker.js",
  . . .
}

Next, deploy your app by running:

npm run deploy

If you’re running the command for the first time, Cloudflare will prompt you to authenticate. Once you’ve done that, it will build and deploy the app to your Cloudflare Workers account.

In some cases, you might run into a TypeScript build error. You can fix this by installing TypeScript and the required type definitions:

npm install --save-dev typescript @types/react @types/node

After that, try running the deploy command again, and it should work without issues.

Set up GitHub integration

Cloudflare lets you set up GitHub integration to automatically build and deploy your app every time you push changes to your Git repository.

To get started, create a new repo for your project. Once the repo is created, copy the remote URL, then open your project folder locally and run the following commands to initialize Git and push your code:

git init
git remote add origin 
git add .
git commit -m "Initial commit"
git push -u origin main

Make sure to replace with the actual URL of your GitHub repository. Once the project is pushed to GitHub, go back to the app you created earlier in your Cloudflare dashboard and navigate to its settings section. Here, under Build & Deploy, you’ll see an option to connect a Git provider:setting up github integration with cloudflare workers

Select the GitHub option and authorize your GitHub account. Once the authorization is successful, choose the repo you just pushed. Now, every time you push to this repo, Cloudflare will automatically build and deploy your app.

Image optimization

Next.js has a built-in component that automatically optimizes images for faster page loads. You can also pass a custom loader to this component, for example, to generate signed URLs, serve images from a custom CDN, or integrate with services like Cloudflare Images.

To use a custom loader with Cloudflare Images, create a new file called imageLoader.ts in the root of your project (same level as package.json) and add the following code:

import type { ImageLoaderProps } from "next/image";

const normalizeSrc = (src: string) => {
  return src.startsWith("/") ? src.slice(1) : src;
};

export default function cloudflareLoader({
  src,
  width,
  quality,
}: ImageLoaderProps) {
  if (process.env.NODE_ENV === "development") {
    return src;
  }
  const params = [`width=${width}`];
  if (quality) {
    params.push(`quality=${quality}`);
  }
  const paramsString = params.join(",");
  return `/cdn-cgi/image/${paramsString}/${normalizeSrc(src)}`;
}

Now, update your next.config.mjs to register the custom loader:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ["fakestoreapi.com"],
    loader: "custom",
    loaderFile: "./imageLoader.ts",
  },
};

export default nextConfig;

// added by create cloudflare to enable calling `getCloudflareContext()` in `next dev`
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
initOpenNextCloudflareForDev();

With this update, Next.js will use your custom loader for all images, allowing you to serve optimized images directly from Cloudflare’s CDN, which improves load times and reduces bandwidth costs.

Comparing Cloudflare and Vercel

Beyond pricing, Cloudflare might be a better alternative to Vercel in other scenarios, too. For example, Cloudflare’s edge network and performance at scale can be more robust than what Vercel offers, especially for global audiences or apps that rely heavily on server-side logic at the edge.

The table below also provides a high-level overview to help you decide which platform might suit your needs better:

Feature Vercel Cloudflare (Workers + OpenNext)
Developer Experience Excellent with zero-config for Next.js Good. More setup required, but getting better
Performance Great, with built-in edge functions Top-tier, with global edge by default
Pricing Can get expensive quickly (especially for Pro/Team plans or high traffic) Much cheaper at scale, generous free tier, pay-as-you-go
Image Optimization Built-in with Needs manual setup (e.g. Cloudflare Images + custom loader)
Customizability Limited and mostly within Vercel’s ecosystem High with full control over routing, caching, edge logic
Use Case Best for fast Next.js projects with minimal config Great for advanced/edge-heavy apps and cost efficiency

Conclusion

In this article, we explored how to build and deploy a Next.js application to Cloudflare Workers using the OpenNext adapter. We walked through setting up the project, creating a simple API route, enabling image optimization with Cloudflare Images, and configuring GitHub for automatic deployments. You can also find the complete code used in this tutorial on GitHub, and preview the final app deployed on Cloudflare here.

Should you switch from Vercel to Cloudflare? If Vercel is working fine and you value ease of use, you can definitely stick with it. However, if you’re running into cost issues or need more control and edge-level performance, Cloudflare is definitely worth considering.

LogRocket: Full visibility into production Next.js apps

Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket captures console logs, errors, network requests, and pixel-perfect DOM recordings from user sessions and lets you replay them as users saw it, eliminating guesswork around why bugs happen — compatible with all frameworks.

LogRocket’s Galileo AI watches sessions for you, instantly identifying and explaining user struggles with automated monitoring of your entire product experience.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

LogRocket Dashboard Free Trial Banner

Modernize how you debug your Next.js apps — start monitoring for free.


Share this content:

I am a passionate blogger with extensive experience in web design. As a seasoned YouTube SEO expert, I have helped numerous creators optimize their content for maximum visibility.

Leave a Comment