Skip to content

[FEATURE] Opt-in flag to strip unused @vercel/og dynamic import (saves ~2 MiB, keeps Worker under free-tier 3 MiB) #1220

@mushan0x0

Description

@mushan0x0

Is your feature request related to a problem?

When an app does not use next/og (ImageResponse, opengraph-image, etc.), the built .open-next/server-functions/default/handler.mjs still contains a dynamic import:

import("next/dist/compiled/@vercel/og/index.edge.js")

This comes from Next.js's internal externalImport helper and survives the existing patchVercelOgImport / patchVercelOgFallbackFont AST rules in packages/cloudflare/src/cli/build/patches/ast/vercel-og.ts, because those rules only rewrite the module specifier (index.node.jsindex.edge.js); they don't strip the call site when @vercel/og is never actually reached at runtime.

Wrangler treats that dynamic import as a real module graph entry and pulls in:

  • @vercel/og edge bundle (~800 KiB uncompressed / ~260 KiB gzip)
  • resvg.wasm (~1.4 MiB)

For a CSR-heavy app that doesn't use OG images at all, this single dynamic import pushes the Worker over the Cloudflare free-tier 3 MiB limit (observed ~3.8 MiB vs ~1.6 MiB after rewrite).

Describe the solution you'd like

Add an opt-in flag to @opennextjs/cloudflare, e.g.:

// open-next.config.ts
export default defineCloudflareConfig({
  cloudflare: { disableVercelOg: true },
});

When enabled, an AST rule under packages/cloudflare/src/cli/build/patches/ast/ rewrites the remaining import("next/dist/compiled/@vercel/og/index.edge.js") call sites to a rejected Promise (or a no-op stub), so Wrangler no longer bundles the @vercel/og edge module and resvg.wasm.

This keeps the default behavior safe and gives free-tier users a supported escape hatch without a post-build regex script.

Happy to send a PR (config flag + AST rule) if maintainers are open to it.

Describe alternatives you've considered

  1. Auto-detect and stub. Scan user sources for next/og / ImageResponse / opengraph-image / twitter-image usage and, when none are found, automatically rewrite the call site. Safer defaults but more moving parts and edge cases.

  2. Post-build regex patch (current workaround). We run a small script before wrangler deploy:

    source = source.replace(
      /import\("next\/dist\/compiled\/@vercel\/og\/index\.edge\.js"\)/g,
      'Promise.reject(new Error("@vercel/og disabled"))',
    );

    After this patch our Worker is ~1.6 MiB instead of ~3.8 MiB and everything else (ProForm, admin UI, D1, R2, KV, Vectorize) works unchanged. But it's fragile — it depends on the exact emitted string and has to live in every project that hits the 3 MiB limit.

  3. Do nothing / upgrade to paid plan. Not acceptable for free-tier / hobby projects that never use OG images.

@opennextjs/cloudflare version

1.19.2

Additional context

Before submitting

  • I have checked that there isn't already a similar feature request
  • This is a single feature (not multiple features in one request)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions