iqfareez
iqfareez

Reputation: 905

Using local assets to generate image using @vercel/og and Next.js

I am using the new @vercel/og library to generate the metatag image. From the official example, they showed how to use image from external source.

The issue

<img
    src={"https://res.cloudinary.com/iqfareez-cloud/image/upload/v1665576747/IIUM%20Schedule/release_meta_kosong_onkkbm.png"}
    alt={"Release background"}
>

However, I want to use the local static image stored in the public directory public/images/release-bg.png. This guide shows an example to access the static assets so I tried it like this:

                <Image
                    src={"/images/release-bg.png"}
                    alt={"Release background"}
                >

Then, I get this warning:

Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)

Followed by this error:

 code: 'ERR_UNHANDLED_ERROR',
  context: TypeError: Cannot read properties of null (reading 'useContext')
      at Object.useContext (webpack-internal:///(middleware)/./node_modules/react/cjs/react.development.js:1619:21)
      at Object.Image [as type] (webpack-internal:///(middleware)/./node_modules/next/dist/client/image.js:38:39)
      at gt (webpack-internal:///(middleware)/./node_modules/satori/dist/esm/index.wasm.js:18:17771)
etc...

Full code

// /pages/api/og/iiumschedule.tsx

import {ImageResponse} from '@vercel/og';
import {NextRequest} from 'next/server';
import Image from 'next/image';

export const config = {
    runtime: 'experimental-edge',
};

export default async function handler(req: NextRequest) {
    const {searchParams} = req.nextUrl;
    const version = searchParams.get('version');
    if (!version) {
        return new ImageResponse(<>Visit with &quot;?username=vercel&quot;</>, {
            width: 1200,
            height: 630,
        });
    }

    return new ImageResponse(
        (
            <div
                style={{
                    display: 'flex',
                    fontSize: 60,
                    color: 'black',
                    background: '#f6f6f6',
                    width: '100%',
                    height: '100%',
                    flexDirection: 'column',
                    justifyContent: 'center',
                    alignItems: 'center',
                }}
            >
                <Image
                    src={"/images/release-bg.png"}
                    alt={"Release background"}
                >
                    <p>github.com/{version}</p>
                </Image>
            </div>
        ),
        {
            width: 1200,
            height: 628,
        },
    );
}

Upvotes: 0

Views: 2720

Answers (2)

Shu Ding
Shu Ding

Reputation: 1675

Because the API runs on the server side, you can't fetch relative URLs (e.g. /path) because the server itself doesn't know about the domain.

You can pass https://my-domain.com/imagepath.png instead.

Also, there is a way to directly bundle inside the API using the same way of font importing, so you don't need to fetch something again on the fly. However if you do that with an image, you will have to convert that ArrayBuffer into inlined base64 data URI and use that as the <img> source. I think Satori will support using ArrayBuffer as the source in the future.

Upvotes: 2

m4china
m4china

Reputation: 240

                <Image
                    src={"/images/release-bg.png"}
                    alt={"Release background"}
                >
                    <p>github.com/{version}</p>
                </Image>

The next.js image function component uses hooks under the hood: https://github.com/vercel/next.js/blob/canary/packages/next/client/image.tsx#L585

You should use the img element as per documentation: https://vercel.com/docs/concepts/functions/edge-functions/og-image-examples

Upvotes: 0

Related Questions