Generate Open Graph Images dynamically from HTML/CSS without a browser.
Powered by @vercel/og.
Install the package by running the following command in terminal:
npm install @cf-wasm/og
- Cloudflare workers:
import * as og from "@cf-wasm/og";
- Next.js (Webpack):
import * as og from "@cf-wasm/og/next";
- CJS (file base):
import * as og from "@cf-wasm/og/node";
If you are using Cloudflare Workers/Pages, you must set the execution context as shown:
import React from "react";
import { ImageResponse, cache } from "@cf-wasm/og";
const handlers: ExportedHandler = {
fetch(req, env, ctx) {
// Set execution context
cache.setExecutionContext(ctx);
return new ImageResponse(<>Hello World!</>);
}
};
export default handlers;
You can load Google fonts and Custom fonts with ease using GoogleFont
and CustomFont
classes respectively:
import React from "react";
import { ImageResponse, GoogleFont, CustomFont, cache } from "@cf-wasm/og";
const handlers: ExportedHandler = {
fetch(req, env, ctx) {
cache.setExecutionContext(ctx);
return new ImageResponse(
(
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: "100%"
}}
>
<p>Text with Default font</p>
<p style={{ fontFamily: "Inclusive Sans" }}>Text with Google font</p>
<p style={{ fontFamily: "JetBrains Mono" }}>Text with Custom font</p>
</div>
),
{
fonts: [
// Using Google font loader
new GoogleFont("Inclusive Sans"),
// Using Custom font loader
new CustomFont("JetBrains Mono", () =>
fetch(
"https://github.com/JetBrains/JetBrainsMono/raw/master/fonts/ttf/JetBrainsMono-Regular.ttf"
).then((res) => res.arrayBuffer())
)
]
}
);
}
};
export default handlers;
You can set global default font using GoogleFont
or CustomFont
classes:
import React from "react";
import {
ImageResponse,
CustomFont,
GoogleFont,
defaultFont,
cache
} from "@cf-wasm/og";
// By using Google font loader
defaultFont.set(new GoogleFont("Merriweather"));
// or
// By using Custom font loader
defaultFont.set(
new CustomFont("JetBrains Mono", () =>
fetch(
"https://github.com/JetBrains/JetBrainsMono/raw/master/fonts/ttf/JetBrainsMono-Regular.ttf"
).then((res) => res.arrayBuffer())
)
);
const handlers: ExportedHandler = {
fetch(req, env, ctx) {
cache.setExecutionContext(ctx);
// It should render with JetBrains Mono font
return new ImageResponse(<>Hello World!</>);
}
};
export default handlers;
You can set default font using defaultFont
option:
import React from "react";
import { ImageResponse, GoogleFont, cache } from "@cf-wasm/og";
const handlers: ExportedHandler = {
fetch(req, env, ctx) {
cache.setExecutionContext(ctx);
return new ImageResponse(<>Hello World!</>, {
// Set default font
defaultFont: new GoogleFont("Merriweather")
});
}
};
export default handlers;
By default ImageResponse
renders to PNG
but you can use format
option to render to SVG
:
import React from "react";
import { ImageResponse, cache } from "@cf-wasm/og";
const handlers: ExportedHandler = {
fetch(req, env, ctx) {
cache.setExecutionContext(ctx);
return new ImageResponse(<>Hello World!</>, {
// Set format
format: "svg"
});
}
};
export default handlers;
The @cf-wasm/og/html-to-react
submodule provides a function t
which can be used to transform html to ReactElement
making it possible to render html as well:
import { ImageResponse, cache } from "@cf-wasm/og";
import { t } from "@cf-wasm/og/html-to-react";
const handlers: ExportedHandler = {
fetch(req, env, ctx) {
cache.setExecutionContext(ctx);
const html = `<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%;">
<p>Renders HTML</p>
<p>Hello World!</p>
</div>`;
return new ImageResponse(t(html));
}
};
export default handlers;
You can also use render
function instead of ImageResponse
to catch errors or for other use cases:
import React from "react";
import { render, cache } from "@cf-wasm/og";
const handlers: ExportedHandler = {
async fetch(req, env, ctx) {
cache.setExecutionContext(ctx);
try {
const { image } = await render(<>Hello World!</>).asPng();
return new Response(image, {
headers: {
"Content-Type": "image/png"
}
});
} catch {
return new Response("Not found", {
status: 404
});
}
}
};
export default handlers;
Following are some examples for using this library:
If you are using Cloudflare Workers, you can use it as shown below:
// src/index.tsx
import React from "react";
import { ImageResponse, GoogleFont, cache } from "@cf-wasm/og";
const handlers: ExportedHandler = {
async fetch(req, env, ctx) {
// Make sure to set the execution context
cache.setExecutionContext(ctx);
const { searchParams } = new URL(req.url);
const parameters = Object.fromEntries(searchParams.entries());
return new ImageResponse(
(
<div
style={{
display: "flex",
flexDirection: "column",
width: "100%",
height: "100%",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#6f90ab",
fontSize: "2rem",
color: "#fff"
}}
>
<span>Noto Sans (Default Font)</span>
<span
style={{
fontFamily: "JetBrains Mono"
}}
>
JetBrains Mono (using GoogleFont class)
</span>
<span>These are emojis: 😎🌩️</span>
<span
style={{
fontFamily: "JetBrains Mono"
}}
>
Parameters: {JSON.stringify(parameters)}
</span>
</div>
),
{
fonts: [new GoogleFont("JetBrains Mono")],
emoji: "fluent"
}
);
}
};
export default handlers;
Warning: If you are using
@cf-wasm/og
onCloudflare Workers
, you may hit the CPU time limit as well as the 1MiB script size limit (Workers Free Plan). Even when using the original project@vercel/og
onCloudflare Pages
, you hit the CPU time limit.
If you are using Next.js (App router) with edge runtime, you can use it as shown below:
// (src/)app/api/og/route.tsx
import React from "react";
import { type NextRequest } from "next/server";
import { ImageResponse, GoogleFont } from "@cf-wasm/og/next";
export const runtime = "edge";
export function GET(req: NextRequest) {
const { searchParams } = req.nextUrl;
const parameters = Object.fromEntries(searchParams.entries());
return new ImageResponse(
(
<div
style={{
display: "flex",
flexDirection: "column",
width: "100%",
height: "100%",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#6f90ab",
fontSize: "2rem",
color: "#fff"
}}
>
<span>Noto Sans (Default Font)</span>
<span
style={{
fontFamily: "JetBrains Mono"
}}
>
JetBrains Mono (using GoogleFont class)
</span>
<span>These are emojis: 😎🌩️</span>
<span
style={{
fontFamily: "JetBrains Mono"
}}
>
Parameters: {JSON.stringify(parameters)}
</span>
</div>
),
{
fonts: [new GoogleFont("JetBrains Mono")],
emoji: "fluent"
}
);
}
If you are using Next.js (Pages router) with edge runtime, you can use it as shown below:
// (src/)pages/api/og.tsx
import React from "react";
import { type NextRequest } from "next/server";
import { ImageResponse, GoogleFont } from "@cf-wasm/og/next";
export const config = {
runtime: "edge"
};
export default async function handler(req: NextRequest) {
const { searchParams } = req.nextUrl;
const parameters = Object.fromEntries(searchParams.entries());
return new ImageResponse(
(
<div
style={{
display: "flex",
flexDirection: "column",
width: "100%",
height: "100%",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#6f90ab",
fontSize: "2rem",
color: "#fff"
}}
>
<span>Noto Sans (Default Font)</span>
<span
style={{
fontFamily: "JetBrains Mono"
}}
>
JetBrains Mono (using GoogleFont class)
</span>
<span>These are emojis: 😎🌩️</span>
<span
style={{
fontFamily: "JetBrains Mono"
}}
>
Parameters: {JSON.stringify(parameters)}
</span>
</div>
),
{
fonts: [new GoogleFont("JetBrains Mono")],
emoji: "fluent"
}
);
}