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 🙏

© 2026 – Pkg Stats / Ryan Hefner

ogsmith

v0.1.0

Published

Typed, deterministic SVG to PNG rendering engine for OG images, social cards, and release banners. Same props, same bytes, in Node and in the browser.

Readme

ogsmith

Typed, deterministic SVG to PNG rendering engine for Open Graph images, social cards, and release banners. Same props, same bytes, in Node, in CI, and in the browser.

Part of the OGsmith project, which also ships a visual editor on top of this engine.

Why this engine

  • Deterministic. Fonts are bundled, templates are pure functions, and there is no browser in the render path. The test suite hashes PNG output and fails CI when a change alters a single byte unintentionally.
  • One rendering path. Preview and export consume the same SVG string. There is no HTML replica that can drift from the exported file.
  • Typed end to end. Every template carries a zod schema; invalid props fail with a readable message before anything renders.

Install

npm install ogsmith
# PNG export in Node:
npm install @resvg/resvg-js
# PNG export in the browser or a worker:
npm install @resvg/resvg-wasm

@resvg/resvg-js and @resvg/resvg-wasm are optional peer dependencies: install only the raster path you use. SVG rendering needs neither.

Render an SVG

import { render, blogPost } from "ogsmith";

const svg = await render(blogPost, {
  title: "Designing a deterministic render pipeline",
  publication: "relay.dev",
  tag: "Engineering",
  author: "Dana Whitfield",
  date: "Jul 2026",
});

Render a PNG (Node)

import { blogPost } from "ogsmith";
import { renderPng } from "ogsmith/raster";
import { writeFile } from "node:fs/promises";

const png = await renderPng(blogPost, blogPost.sample, { theme: "midnight" });
await writeFile("og.png", png);

Render a PNG (browser or worker)

import { productLaunch } from "ogsmith";
import { initWasmRaster, renderPngWasm } from "ogsmith/raster-wasm";
import wasmUrl from "@resvg/resvg-wasm/index_bg.wasm?url";

await initWasmRaster(fetch(wasmUrl));
const png = await renderPngWasm(productLaunch, productLaunch.sample);

In the browser, pass fonts explicitly (fetch the files shipped under ogsmith/fonts/) via the fonts render option; Node loads them from disk automatically.

Built-in templates

| ID | Size | Purpose | | ---------------- | --------- | -------------------------------------- | | blog-post | 1200×630 | OG image for an article | | product-launch | 1200×630 | OG image for a product or landing page | | release-banner | 1600×900 | Version release announcement | | quote-card | 1080×1080 | Square statement card |

All templates support the four built-in themes: graphite, paper, midnight, and sand. Every template/theme pair is verified for WCAG AA contrast by an automated test.

Custom templates

import { z } from "zod";
import { defineTemplate, registerTemplate, box, text, render } from "ogsmith";

const schema = z.object({ heading: z.string().min(1).max(60) });

const myCard = registerTemplate(
  defineTemplate<z.infer<typeof schema>>({
    id: "my-card",
    name: "My card",
    description: "A minimal custom card.",
    size: { width: 1200, height: 630 },
    schema,
    fields: { heading: { label: "Heading", control: "text" } },
    sample: { heading: "Hello from a custom template" },
    render: (props, theme) =>
      box(
        {
          width: "100%",
          height: "100%",
          alignItems: "center",
          justifyContent: "center",
          backgroundColor: theme.tokens.bg,
          fontFamily: "Inter",
        },
        text(
          { fontSize: 72, fontWeight: 800, color: theme.tokens.ink },
          props.heading,
        ),
      ),
  }),
);

const svg = await render(myCard, { heading: "Ship it" });

Templates must stay pure: no Date.now(), no randomness, no environment reads. That is what keeps output reproducible byte for byte.

API summary

| Export | Description | | ------ | ----------- | | render(template, props, options?) | Template to SVG string | | renderPng(template, props, options?) | Template to PNG bytes (Node, ogsmith/raster) | | renderPngWasm(template, props, options?) | Template to PNG bytes (browser, ogsmith/raster-wasm) | | initWasmRaster(source) | One-time wasm initialization for the browser path | | templates via listTemplates() / getTemplate(id) | Template registry | | defineTemplate / registerTemplate | Custom template authoring | | themes, getTheme, themeNames | Built-in theme tokens | | contrastRatio, meetsAA | WCAG contrast utilities | | loadBuiltinFonts, createFontSet, builtinFontSpecs | Font handling |

options.theme accepts a built-in theme name or a full custom Theme object. options.fonts overrides the font set (required in browsers).

Fonts and licensing

Inter and JetBrains Mono are bundled under the SIL Open Font License; the license texts ship inside the package (fonts/OFL-*.txt). The engine never loads system fonts or fetches from the network.

License

MIT © Fabio Rosa