How to migrate MemFree from Vercel to Cloudflare next-on-pages

How to migrate MemFree from Vercel to Cloudflare next-on-pages

Deploying a Static Next.js App on Cloudflare Pages

Deploying a static Next.js application on Cloudflare Pages is straightforward. You can accomplish this with just a few commands by following the guide on Cloudflare's documentation for deploying a static Next.js site.

To validate this process, I first extracted the React review feature from the MemFree AI Page Generator and deployed it as a standalone webpage: React Shadcn UI Preview.

The entire deployment process was smooth, with minimal obstacles encountered.

Deploying MemFree AI Page Generator on Cloudflare

You can experience the online version here: PageGen - AI Page Generator.

  1. Challenge 1: All APIs Support for Edge Runtime

  2. Challenge 2: All Dynamic Pages Support for Edge Runtime

Specific Cloudflare Page Edge Compatibility Issues

1. Custom Format Replacement for util.format

To ensure compatibility with the edge environment, replace the util.format function with the following implementation:

// Replace the util.format function with this one; this method works in the edge environment
export function format(template, ...args) {
    if (args.length === 0) return template;
 
    if (args.length === 1 && typeof args[0] === 'object') {
        return template.replace(/%[sdj]/g, (match) => String(args[0]));
    }
 
    let index = 0;
    return template.replace(/%[sdj]/g, () => {
        if (index >= args.length) return '';
        const arg = args[index++];
        return String(arg);
    });
}

2. Replace @upstash/redis with @upstash/redis/cloudflare

Using @upstash/redis directly may result in the following error:

Failed to get searches: Error: The 'cache' field on 'RequestInitializerDict' is not implemented

To resolve this, simply replace the import statement:

import { Redis } from '@upstash/redis/cloudflare';

No other code modifications are necessary.

3. Replace pdfjs with unpdf

The pdfjs library, widely used for PDF parsing, is incompatible with the edge environment. If you attempt to use pdfjs, you may encounter the following errors during deployment:

⚡️ Unexpected error: Build failed with 4 errors:
⚡️ <stdin>:883:64363: ERROR: Could not resolve "fs"
⚡️ <stdin>:883:64423: ERROR: Could not resolve "http"
⚡️ <stdin>:883:64450: ERROR: Could not resolve "https"
⚡️ <stdin>:883:64476: ERROR: Could not resolve "url"

The reason for these errors is that the edge environment does not support certain Node.js modules. The solution is to use unpdf instead.

4. Replace uploadthing with R2

uploadthing is a component designed to simplify client-side file uploads across various frameworks, but it does not support the Cloudflare edge environment.

When using Cloudflare Pages or Workers, you can easily upload files to R2 with just a few lines of code, benefiting from free CDN acceleration:

const res = await getRequestContext().env.IMAGES.put(safeFileName, file);

5. Use Static Variables Instead of NEXT_PUBLIC Environment Variables

In Cloudflare's Next-on-Pages, NEXT_PUBLIC environment variables cannot be accessed directly. You can replace them with static variables instead.

6. Replace contentlayer2 with next/mdx

The documentation and blog sections of MemFree were generated using contentlayer2 based on MDX files. However, using contentlayer2 in Cloudflare Next-on-Pages results in the following error:

EvalError: Code generation from strings disallowed for this context

The reason for the error with contentlayer2 is that it uses the Function constructor, which is not allowed in edge environments.

export const getMDXComponent = (code: string, globals: Record<string, unknown> = {}): React.FC<MDXContentProps> => {
    const scope = { React, ReactDOM, _jsx_runtime, ...globals };
    const fn = new Function(...Object.keys(scope), code);
    return fn(...Object.values(scope)).default;
};
 
export const useMDXComponent = (code: string, globals: Record<string, unknown> = {}) => {
    return React.useMemo(() => getMDXComponent(code, globals), [code, globals]);
};

However, this is actually unnecessary because static pages can be fully generated during the build process without the need for runtime dynamic rendering. In the end, I replaced contentlayer2 with next/mdx, which completely replaces mdx with static pages during the compilation phase.

For more details, refer to Markdown and MDX.

You can see the results on PageGen's Privacy Policy and Terms of Service pages.

7. Use Bundle Analyzer to Reduce Bundle Size

One issue arises when enabling edge runtime for dynamic pages, as some large client dependencies may inadvertently enter the runtime's dependency tree:

│ __next-on-pages-dist__/functions/search/[id].func.js                │ esm  │ 8995.45 KiB │

For instance, the size of the search page can reach around 8 MB. By analyzing the bundle, we identified two large client packages: @babel/standalone and heic2any.

We addressed this by dynamically importing these packages, enabling lazy loading and singleton instantiation, which reduced the overall project size from 25 MB to just 2 MB.

8. Use Cloudflare Image Loader

Integrating Cloudflare's Image Loader is straightforward. Follow the steps outlined in Cloudflare Image Transform integration with Next.js. The core steps include:

  1. Defining imageLoader.ts in your code.
  2. Enabling Cloudflare's image transformation service for your domain Cloudflare Image Transform Image.

9. Domain Binding and Redirection

The domain binding and redirection settings differ between Cloudflare Pages and Vercel. For a Pages project, you need to bind both the www and root domains to the Pages project. Then, use Cloudflare's built-in redirection rules to redirect the www domain to the root domain.

Online Experience

You can explore the live version here: PageGen - AI Page Generator.

All source code for MemFree is open-source, and contributions are welcome! Feel free to give it a star on GitHub: MemFree GitHub.