npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@cf-wasm/og

v0.1.31

Published

Generate Open Graph Images dynamically from HTML/CSS without a browser.

Downloads

673

Readme

@cf-wasm/og

Generate Open Graph Images dynamically from HTML/CSS without a browser.

Powered by @vercel/og.

[!WARNING] Breaking changes may be introduced without following semantic versioning.
Please note that this package may undergo breaking changes between versions without adhering strictly to semantic versioning (SemVer). While efforts will be made to minimize disruptions, it's recommended to review release notes carefully before upgrading to a new version. We apologize for any inconvenience this may cause and appreciate your understanding.

Installation

npm install @cf-wasm/og       # npm
yarn add @cf-wasm/og          # yarn
pnpm add @cf-wasm/og          # pnpm

Usage

  • Cloudflare Workers / Pages (Esbuild):

    import { ImageResponse } from "@cf-wasm/og";
  • Next.js Edge Runtime (Webpack):

    import { ImageResponse } from "@cf-wasm/og/next";
  • Node.js (file base):

    import { ImageResponse } from "@cf-wasm/og/node";

Documentation

Cloudflare Workers/Pages

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";

export default {
  fetch(req, env, ctx) {
    // Set execution context
    cache.setExecutionContext(ctx);

    return new ImageResponse(<>Hello World!</>);
  }
} satisfies ExportedHandler;

Font Loaders

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";

export default {
  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())
          )
        ]
      }
    );
  }
} satisfies ExportedHandler;

Default font

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())
  )
);

export default {
  fetch(req, env, ctx) {
    cache.setExecutionContext(ctx);

    // It should render with JetBrains Mono font
    return new ImageResponse(<>Hello World!</>);
  }
} satisfies ExportedHandler;

You can set default font using defaultFont option:

import React from "react";
import { ImageResponse, GoogleFont, cache } from "@cf-wasm/og";

export default {
  fetch(req, env, ctx) {
    cache.setExecutionContext(ctx);

    return new ImageResponse(<>Hello World!</>, {
      // Set default font
      defaultFont: new GoogleFont("Merriweather")
    });
  }
} satisfies ExportedHandler;

SVG format

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";

export default {
  fetch(req, env, ctx) {
    cache.setExecutionContext(ctx);

    return new ImageResponse(<>Hello World!</>, {
      // Set format
      format: "svg"
    });
  }
} satisfies ExportedHandler;

Render HTML

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";

export default {
  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));
  }
} satisfies ExportedHandler;

render function

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";

export default {
  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
      });
    }
  }
} satisfies ExportedHandler;

Examples

Following are some examples for using this library:

Cloudflare Workers

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";

export default {
  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"
      }
    );
  }
} satisfies ExportedHandler;

[!WARNING] If you are using @cf-wasm/og on Cloudflare 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 on Cloudflare Pages, you hit the CPU time limit.

Next.js (App Router)

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"
    }
  );
}

Next.js (Pages Router)

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"
    }
  );
}