#og

1 messages · Page 1 of 1 (latest)

vague magnetBOT
#

Hi @jaunty hound. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

The error is happening because searchParams is not a supported prop for opengraph-image in Next.js. For opengraph-image and twitter-image, the only documented prop is params (for dynamic route segments), and in Next.js 16 it is a promise. [OG props; Async params change]

So this signature is not valid:

export default async function Image({
  searchParams,
}: {
  searchParams: Promise<{ date?: string }>
}) {
  const { date } = await searchParams
}

searchParams will be undefined, which is why destructuring fails.

How to fix

For an OG image under /api/leaderboard/opengraph-image, you have two options:

  1. Use the request URL (recommended for API-style OG routes)
    Instead of opengraph-image.tsx, use a route handler like in Vercel’s OG docs and read the query from request.url:

    // app/api/leaderboard/opengraph-image/route.tsx
    import { ImageResponse } from 'next/og'
    
    export async function GET(request: Request) {
      const { searchParams } = new URL(request.url)
      const date = searchParams.get('date')
    
      if (!date) {
        return new Response('Missing date parameter', { status: 400 })
      }
    
      return new ImageResponse(
        <div>{date}</div>,
        { width: 1200, height: 630 },
      )
    }
    

    This pattern is exactly how Vercel shows passing ?title= as a URL parameter for OG images. [Dynamic text OG]

  2. If you really need the opengraph-image file convention
    You can only receive params (dynamic segments), not searchParams. The documented signature is:

#
   export default async function Image({
     params,
   }: {
     params: Promise<{ slug: string }>
   }) {
     const { slug } = await params
     // ...
   }

[OG props]

In this case, query string like ?date=... is not available via searchParams prop.

Given your URL (/api/leaderboard/opengraph-image?date=...), switching to a route.tsx handler and reading new URL(request.url).searchParams is the correct and supported way to access date.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@jaunty hound Kapa.ai is still learning and improving, please let me know how I did by reacting below.

jaunty hound
vague magnetBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

You’re generating an OG image at /api/leaderboard/opengraph-image and want to cache it because it’s heavy.

From the docs, there are two relevant caching layers:

  1. Next.js Data/HTML cache (App Router OG routes)
  2. Vercel CDN cache via Cache-Control headers

1. Use the built‑in OG route caching

opengraph-image.tsx is a special Route Handler that is cached by default unless you use Dynamic APIs or dynamic config. [OG route caching]

So if your opengraph-image.tsx:

  • Only uses cached fetch (e.g. cache: 'force-cache' or next: { revalidate: ... }), or
  • Only uses static data,

then Next.js will statically optimize and cache the generated image automatically. [OG route caching]

To make heavy data fetching cheaper, cache that data:

const res = await fetch('<https://your-api/leaderboard',> {
  cache: 'force-cache',
  next: { revalidate: 3600 }, // revalidate every hour
})
const data = await res.json()

This uses the Data Cache and revalidates in the background (stale‑while‑revalidate style). [Data cache; Time-based revalidate]


2. If you use an API route (/api/og) instead

#

If you switch to an API route (e.g. app/api/leaderboard/opengraph-image/route.tsx returning new ImageResponse(...)), Vercel will automatically cache OG images on the CDN when using @vercel/og / next/og. [Vercel OG benefits; Next.js on Vercel OG]

You can further control CDN caching with Cache-Control headers from the route handler (it’s just a function response):

import { ImageResponse } from 'next/og'

export const runtime = 'nodejs' // if needed

export async function GET(request: Request) {
  // heavy work here...

  const image = new ImageResponse(<div>...</div>, {
    width: 1200,
    height: 630,
  })

  // Wrap in a Response to add headers
  return new Response(image.body, {
    status: 200,
    headers: {
      'Content-Type': 'image/png',
      // cache 10 minutes, serve stale while revalidating 50 minutes
      'Cache-Control': 'public, s-maxage=600, stale-while-revalidate=3000',
    },
  })
}

Vercel’s CDN will then cache this response according to s-maxage / stale-while-revalidate. [How to cache responses; stale-while-revalidate]


3. Summary of practical options

#
  • Inside the OG route:
    • Cache heavy data with fetch(..., { cache: 'force-cache', next: { revalidate: N } }). [Next fetch caching]
  • At the image level:
    • If using opengraph-image.tsx file convention, rely on its default static optimization and caching (avoid dynamic APIs where possible). [OG route caching]
    • If using an API route (route.tsx), add Cache-Control: s-maxage=..., stale-while-revalidate=... so Vercel’s CDN caches the generated image. [How to cache responses]

If you share your current opengraph-image.tsx or route.tsx, I can point out exactly where to add fetch caching and/or headers.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: