og-pilot-js
v0.3.2
Published
JavaScript/TypeScript client for the OG Pilot Open Graph image generator.
Maintainers
Readme
OG Pilot JS
[!IMPORTANT]
An active OG Pilot subscription is required to use this package.
A small JavaScript/TypeScript client for generating OG Pilot Open Graph images via signed JWTs.
Installation
npm install og-pilot-jsConfiguration
The default client reads from OG_PILOT_API_KEY and OG_PILOT_DOMAIN.
You can override them at runtime:
import { configure } from "og-pilot-js";
configure((config) => {
config.apiKey = process.env.OG_PILOT_API_KEY;
config.domain = process.env.OG_PILOT_DOMAIN;
// Optional overrides:
// config.openTimeoutMs = 5000;
// config.readTimeoutMs = 10000;
// config.stripExtensions = true;
});Usage
Generate an image URL (follows the redirect returned by OG Pilot):
import { createImage } from "og-pilot-js";
const imageUrl = await createImage({
template: "blog_post",
title: "How to Build Amazing OG Images",
description: "A complete guide to social media previews",
bg_color: "#1a1a1a",
text_color: "#ffffff",
author_name: "Jane Smith",
publish_date: "2024-01-15"
}, {
iat: Date.now() // optional; refresh cache daily
});If you omit iat, OG Pilot will cache the image indefinitely. Provide an iat
to refresh the cache daily. You can pass a Date, epoch seconds, or epoch
milliseconds (Date.now() is auto-converted).
Template helpers
createImage defaults to the page template when template is omitted.
Use these helpers to force a specific template:
createBlogPostImage(...)createPodcastImage(...)createProductImage(...)createEventImage(...)createBookImage(...)createCompanyImage(...)createPortfolioImage(...)
import { createBlogPostImage } from "og-pilot-js";
const imageUrl = await createBlogPostImage({
title: "How to Build Amazing OG Images",
author_name: "Jane Smith",
publish_date: "2024-01-15"
});Parameters
All parameters are embedded in the signed JWT payload; the only query param is token.
The library handles iss (domain) and sub (API key prefix) automatically.
Core parameters
| Parameter | Required | Default | Description |
|---------------|----------|----------|---------------------------------------------------------------|
| template | No | "page" | Template name |
| title | Yes | — | Primary title text |
| description | No | — | Subtitle or supporting text |
| logo_url | No | — | Logo image URL |
| image_url | No | — | Hero image URL |
| bg_color | No | — | Background color (hex format) |
| text_color | No | — | Text color (hex format) |
| iat | No | — | Issued-at timestamp for daily cache busting |
| path | No | auto-set | Request path for image rendering context (see Path handling) |
Configuration options
| Option | Default | Description |
|-------------------|---------------------------|--------------------------------------------------------------------------|
| apiKey | OG_PILOT_API_KEY env var | Your OG Pilot API key |
| domain | OG_PILOT_DOMAIN env var | Your domain registered with OG Pilot |
| baseUrl | https://ogpilot.com | OG Pilot API base URL |
| openTimeoutMs | 5000 | Connection timeout in milliseconds |
| readTimeoutMs | 10000 | Read timeout in milliseconds |
| stripExtensions | true | When true, file extensions are stripped from resolved paths (see Strip extensions) |
| fetch | global fetch | Custom fetch implementation |
Options
| Option | Default | Description |
|-----------|---------|--------------------------------------------------------------------------|
| json | false | When true, sends Accept: application/json and parses the JSON response |
| headers | — | Additional HTTP headers to include with the request |
| default | false | Forces path to / when true, unless a manual path is provided |
Path handling
The path parameter enhances OG Pilot analytics by tracking which OG images perform better across different pages on your site. By capturing the request path, you get granular insights into click-through rates and engagement for each OG image.
The client automatically injects a path parameter on every request:
| Option | Behavior |
|------------------|---------------------------------------------------------------------------------------------------------|
| default: false | Uses the current request path when available (via request context), then falls back to env vars, then / |
| default: true | Forces the path parameter to /, regardless of the current request (unless path is provided explicitly) |
| path: "/..." | Uses the provided path verbatim (normalized to start with /), overriding auto-resolution |
Framework Integration
Next.js (App Router)
In Next.js, the recommended approach is to use generateMetadata and build the
path directly from params and searchParams (no middleware needed):
// app/products/[id]/page.tsx
import type { Metadata } from "next";
import { buildPathFromNextProps, createImage } from "og-pilot-js";
export async function generateMetadata(props): Promise<Metadata> {
const path = await buildPathFromNextProps("/products/[id]", props);
const imageUrl = await createImage(
{
template: "product",
title: "Product page",
},
{ path }
);
return {
openGraph: {
images: [imageUrl],
},
};
}For catch-all routes:
// app/blog/[...slug]/page.tsx
import { buildPathFromNextProps } from "og-pilot-js";
export async function generateMetadata(props) {
const path = await buildPathFromNextProps("/blog/[...slug]", props);
// => /blog/2024/launch?ref=twitter
}Express
import express from "express";
import { createExpressMiddleware } from "og-pilot-js";
const app = express();
app.use(createExpressMiddleware());
// Now path is automatically captured in all routes
app.get("/blog/:slug", async (req, res) => {
const imageUrl = await createImage({
template: "blog_post",
title: "My Blog Post",
});
// path is automatically set to /blog/:slug
});Nuxt (useSeoMeta)
Nuxt recommends useSeoMeta for SEO tags. You can generate the OG image URL
server-side and pass it directly:
<!-- app/pages/products/[id].vue -->
<script setup lang="ts">
import { createImage } from "og-pilot-js";
const route = useRoute();
if (import.meta.server) {
const imageUrl = await createImage(
{
template: "product",
title: "Product page",
},
{ path: route.fullPath }
);
useSeoMeta({
title: "Product page",
ogTitle: "Product page",
ogImage: imageUrl,
twitterCard: "summary_large_image",
});
}
</script>If you need reactive metadata, pass a computed getter:
<script setup lang="ts">
const title = ref("My title");
useSeoMeta({
title,
ogTitle: () => title.value,
});
</script>Other Frameworks
For SvelteKit, Remix, or other frameworks, use withRequestContext in your server middleware:
// SvelteKit hooks (src/hooks.server.ts)
import { setCurrentRequest } from "og-pilot-js";
import type { Handle } from "@sveltejs/kit";
export const handle: Handle = async ({ event, resolve }) => {
setCurrentRequest({ url: event.url.pathname + event.url.search });
return resolve(event);
};Using withRequestContext (async-safe)
For fine-grained control or when middleware isn't suitable:
import { withRequestContext, createImage } from "og-pilot-js";
// In your request handler
await withRequestContext({ url: req.originalUrl }, async () => {
const imageUrl = await createImage({
template: "blog_post",
title: "My Blog Post"
});
// path is automatically set to req.originalUrl
});Manual path override
const imageUrl = await createImage({
template: "page",
title: "Hello OG Pilot",
path: "/pricing?plan=pro"
});Default path
const imageUrl = await createImage(
{
template: "blog_post",
title: "Default OG Image"
},
{ default: true }
);
// path is set to "/"Fetch JSON metadata instead:
import { createImage } from "og-pilot-js";
const data = await createImage(
{
template: "page",
title: "Hello OG Pilot"
},
{ json: true }
);Strip extensions
When stripExtensions is enabled, the client removes file extensions from the
last segment of every resolved path. This ensures that /docs, /docs.md,
/docs.php, and /docs.html all resolve to "/docs", so analytics are
consolidated under a single path regardless of the URL extension.
Multiple extensions are also stripped (/archive.tar.gz becomes /archive).
Dotfiles like /.hidden are left unchanged. Query strings are preserved.
import { configure, createImage } from "og-pilot-js";
configure((config) => {
config.stripExtensions = true;
});
// All of these resolve to path "/docs":
await createImage({ title: "Docs", path: "/docs" });
await createImage({ title: "Docs", path: "/docs.md" });
await createImage({ title: "Docs", path: "/docs.php" });
// Nested paths work too: /blog/my-post.html → /blog/my-post
// Query strings are preserved: /docs.md?ref=main → /docs?ref=main
// Dotfiles are unchanged: /.hidden stays /.hiddenOr pass it when creating a client:
const ogPilot = createClient({
apiKey: "...",
domain: "...",
stripExtensions: true,
});Framework notes
This library is meant for server-side usage. Keep your API key private and do not call it from client-side/browser code. In Next.js or Nuxt, call it from API routes or server handlers.
Advanced usage
Create a dedicated client with custom configuration:
import { createClient } from "og-pilot-js";
const ogPilot = createClient({
apiKey: process.env.OG_PILOT_API_KEY,
domain: process.env.OG_PILOT_DOMAIN
});
const url = await ogPilot.createImage({ title: "Hello" });Development
npm run build
npm run typecheck