@ogify/core 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -86
- package/dist/index.d.mts +77 -72
- package/dist/index.d.ts +77 -72
- package/dist/index.js +318 -51
- package/dist/index.mjs +295 -51
- package/package.json +14 -26
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -1,97 +1,113 @@
|
|
|
1
|
-
# OGify -
|
|
1
|
+
# OGify - Generate beautiful OG images in minutes
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/%40ogify%2Fcore)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Zero-config dynamic Open Graph images for Next.js, Nuxt, Remix, and more. Just copy & paste the production-ready templates.
|
|
8
|
+
|
|
9
|
+

|
|
8
10
|
|
|
9
11
|
## ⚡ Why OGify?
|
|
10
12
|
|
|
11
|
-
- 🔌 **Zero-config**: Works out of the box with Next.js, Remix,
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- 🖼️ **
|
|
15
|
-
- ⚡ **Smart caching**:
|
|
16
|
-
-
|
|
13
|
+
- 🔌 **Zero-config**: Works out of the box with Next.js, Remix, Nuxt, and more.
|
|
14
|
+
- 🔤 **Hassle-free assets**: Just specify a Google Font name, Emoji provider - no downloads, no font files, no hassle.
|
|
15
|
+
- 🎨 **Flexible Customization**: Intuitive API & Tailwind-like syntax helps building eye-catching templates faster.
|
|
16
|
+
- 🖼️ **Production-ready templates**: OGify provides a set of production-ready templates with zero configuration.
|
|
17
|
+
- ⚡ **Smart caching**: Automatically caches fonts, emojis, and generated images - no configuration required.
|
|
18
|
+
- 🌍 **RTL Support**: Built-in support for Right-to-Left languages like Arabic, Hebrew, and Persian.
|
|
17
19
|
|
|
18
20
|
## 📦 Installation
|
|
19
21
|
|
|
20
22
|
```bash
|
|
21
|
-
pnpm add @ogify/core
|
|
23
|
+
pnpm add @ogify/core @ogify/templates
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
or
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @ogify/core @ogify/templates
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
or
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
yarn add @ogify/core @ogify/templates
|
|
22
36
|
```
|
|
23
37
|
|
|
24
38
|
## 🚀 Quick Start
|
|
25
39
|
|
|
26
|
-
### 1.
|
|
40
|
+
### 1. Choose a Template
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
OGify comes with a collection of beautiful, production-ready templates. Check out [our gallery](https://ogify.dev/templates) for more.
|
|
29
43
|
|
|
30
44
|
```typescript
|
|
31
|
-
import
|
|
32
|
-
|
|
33
|
-
const blogTemplate = defineTemplate({
|
|
34
|
-
id: 'blog-post',
|
|
35
|
-
name: 'Blog Post',
|
|
36
|
-
description: 'Template for blog post OG images',
|
|
37
|
-
fonts: [
|
|
38
|
-
{ name: 'Inter', weight: 700 },
|
|
39
|
-
{ name: 'Inter', weight: 400 }
|
|
40
|
-
],
|
|
41
|
-
renderer: ({ params }: OgTemplateOptions) => {
|
|
42
|
-
return `
|
|
43
|
-
<div style="display: flex; flex-direction: column; width: 100%; height: 100%; background: white; padding: 40px;">
|
|
44
|
-
<h1 style="font-size: 60px; font-weight: 700; color: #000;">
|
|
45
|
-
${params.title}
|
|
46
|
-
</h1>
|
|
47
|
-
<p style="font-size: 30px; color: #666;">
|
|
48
|
-
${params.description}
|
|
49
|
-
</p>
|
|
50
|
-
</div>
|
|
51
|
-
`;
|
|
52
|
-
},
|
|
53
|
-
});
|
|
45
|
+
import template from '@ogify/templates/basic';
|
|
46
|
+
import type { TemplateParams } from '@ogify/templates/basic';
|
|
54
47
|
```
|
|
55
48
|
|
|
56
49
|
### 2. Create a Renderer
|
|
57
50
|
|
|
58
|
-
Create a renderer instance and register your
|
|
51
|
+
Create a renderer instance and register your template:
|
|
59
52
|
|
|
60
53
|
```typescript
|
|
61
|
-
import {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
import { createRenderer } from '@ogify/core';
|
|
55
|
+
import template from '@ogify/templates/basic';
|
|
56
|
+
import type { TemplateParams } from '@ogify/templates/basic';
|
|
57
|
+
|
|
58
|
+
const renderer = createRenderer<{ basic: TemplateParams }>({
|
|
59
|
+
templates: { basic: template },
|
|
60
|
+
sharedParams: {
|
|
61
|
+
brandLogo: 'https://ogify.dev/logo.svg',
|
|
62
|
+
brandName: 'OGify',
|
|
67
63
|
}
|
|
68
64
|
});
|
|
69
65
|
```
|
|
70
66
|
|
|
71
67
|
### 3. Generate an Image
|
|
72
68
|
|
|
73
|
-
Render
|
|
69
|
+
Render the template to a PNG buffer:
|
|
74
70
|
|
|
75
71
|
```typescript
|
|
76
72
|
// Basic usage (1200x630)
|
|
77
|
-
const imageBuffer = await renderer.renderToImage('
|
|
73
|
+
const imageBuffer = await renderer.renderToImage('basic', {
|
|
78
74
|
title: 'Hello World',
|
|
79
|
-
|
|
75
|
+
subtitle: 'My first OG image',
|
|
76
|
+
layout: 'centered', // aligned | centered | split
|
|
77
|
+
cta: 'Read More',
|
|
78
|
+
primaryColor: '#000000',
|
|
80
79
|
});
|
|
81
80
|
|
|
82
81
|
// Custom dimensions for Twitter (1200x675)
|
|
83
|
-
const twitterImage = await renderer.renderToImage('
|
|
84
|
-
title: 'Hello World'
|
|
82
|
+
const twitterImage = await renderer.renderToImage('basic', {
|
|
83
|
+
title: 'Hello World',
|
|
84
|
+
subtitle: 'My first OG image',
|
|
85
|
+
layout: 'split',
|
|
85
86
|
}, {
|
|
86
87
|
width: 1200,
|
|
87
88
|
height: 675
|
|
88
89
|
});
|
|
90
|
+
|
|
91
|
+
// RTL Support (Arabic/Hebrew/Persian)
|
|
92
|
+
const rtlImage = await renderer.renderToImage('basic', {
|
|
93
|
+
title: 'مرحبا بالعالم',
|
|
94
|
+
subtitle: 'هذه أول صورة OG لي',
|
|
95
|
+
layout: 'aligned',
|
|
96
|
+
}, {
|
|
97
|
+
isRTL: true
|
|
98
|
+
});
|
|
89
99
|
```
|
|
90
100
|
|
|
91
101
|
**That's it!** You're ready for production. No font files to download, no build configuration, no asset pipeline.
|
|
92
102
|
|
|
93
103
|
## 🚀 Time-Saving Features
|
|
94
104
|
|
|
105
|
+
### **Rich Templates**
|
|
106
|
+
|
|
107
|
+
Access a growing library of beautiful, production-ready templates. [Browse Templates](https://ogify.dev/templates)
|
|
108
|
+
|
|
109
|
+
**Time saved**: Hours of design and implementation time
|
|
110
|
+
|
|
95
111
|
### **Zero-Config Google Fonts**
|
|
96
112
|
|
|
97
113
|
Traditional approach (manual setup):
|
|
@@ -99,23 +115,24 @@ Traditional approach (manual setup):
|
|
|
99
115
|
```typescript
|
|
100
116
|
// ❌ Manual: Download fonts, manage files, configure paths
|
|
101
117
|
import fs from 'fs';
|
|
102
|
-
|
|
103
|
-
const
|
|
118
|
+
|
|
119
|
+
const fontData = fs.readFileSync('./fonts/Inter-Regular.ttf');
|
|
120
|
+
const font2Data = fs.readFileSync('./fonts/Inter-Bold.ttf');
|
|
104
121
|
|
|
105
122
|
const fonts = [
|
|
106
|
-
{ name: 'Inter', data: fontData, weight:
|
|
107
|
-
{ name: 'Inter', data: font2Data, weight:
|
|
123
|
+
{ name: 'Inter', data: fontData, weight: 400 },
|
|
124
|
+
{ name: 'Inter', data: font2Data, weight: 700 }
|
|
108
125
|
];
|
|
109
126
|
```
|
|
110
127
|
|
|
111
|
-
OGify approach (zero
|
|
128
|
+
OGify approach (zero-config):
|
|
112
129
|
|
|
113
130
|
```typescript
|
|
114
131
|
// ✅ OGify: Just specify the font name - we handle the rest
|
|
115
132
|
const template = defineTemplate({
|
|
116
133
|
fonts: [
|
|
117
|
-
{ name: 'Inter', weight:
|
|
118
|
-
{ name: 'Inter', weight:
|
|
134
|
+
{ name: 'Inter', weight: 400 }, // Automatically loaded from Google Fonts
|
|
135
|
+
{ name: 'Inter', weight: 700 }
|
|
119
136
|
],
|
|
120
137
|
// ...
|
|
121
138
|
});
|
|
@@ -127,13 +144,13 @@ const template = defineTemplate({
|
|
|
127
144
|
|
|
128
145
|
```typescript
|
|
129
146
|
// First render: Downloads fonts from Google (~500ms)
|
|
130
|
-
await renderer.renderToImage('
|
|
147
|
+
await renderer.renderToImage('basic', { title: 'Post 1', subtitle: '...' });
|
|
131
148
|
|
|
132
149
|
// Second render: Uses cached fonts (~50ms) - 10x faster! ⚡
|
|
133
|
-
await renderer.renderToImage('
|
|
150
|
+
await renderer.renderToImage('basic', { title: 'Post 2', subtitle: '...' });
|
|
134
151
|
|
|
135
152
|
// All subsequent renders: Lightning fast
|
|
136
|
-
await renderer.renderToImage('
|
|
153
|
+
await renderer.renderToImage('basic', { title: 'Post 3', subtitle: '...' });
|
|
137
154
|
```
|
|
138
155
|
|
|
139
156
|
**Performance**: 10x faster after first render, no configuration needed
|
|
@@ -152,6 +169,33 @@ renderer: ({ params }) => `
|
|
|
152
169
|
|
|
153
170
|
**Time saved**: No emoji sprite sheets, no asset management, no build step
|
|
154
171
|
|
|
172
|
+
## 🛠️ Creating Custom Templates
|
|
173
|
+
|
|
174
|
+
If you want to build your own templates from scratch, you can use `defineTemplate`.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { defineTemplate, OgTemplateOptions } from '@ogify/core';
|
|
178
|
+
|
|
179
|
+
const blogTemplate = defineTemplate({
|
|
180
|
+
fonts: [
|
|
181
|
+
{ name: 'Inter', weight: 400 },
|
|
182
|
+
{ name: 'Inter', weight: 700 }
|
|
183
|
+
],
|
|
184
|
+
renderer: ({ params }: OgTemplateOptions) => {
|
|
185
|
+
return `
|
|
186
|
+
<div style="display: flex; flex-direction: column; width: 100%; height: 100%; background: white; padding: 40px;">
|
|
187
|
+
<h1 style="font-size: 60px; font-weight: 700; color: #000;">
|
|
188
|
+
${params.title}
|
|
189
|
+
</h1>
|
|
190
|
+
<p style="font-size: 30px; color: #666;">
|
|
191
|
+
${params.description}
|
|
192
|
+
</p>
|
|
193
|
+
</div>
|
|
194
|
+
`;
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
155
199
|
## 📚 Advanced Usage
|
|
156
200
|
|
|
157
201
|
### Custom Fonts
|
|
@@ -160,9 +204,6 @@ Load fonts from Google Fonts, custom URLs, or embedded data:
|
|
|
160
204
|
|
|
161
205
|
```typescript
|
|
162
206
|
const template = defineTemplate({
|
|
163
|
-
id: 'custom-fonts',
|
|
164
|
-
name: 'Custom Fonts Template',
|
|
165
|
-
description: 'Template with custom fonts',
|
|
166
207
|
fonts: [
|
|
167
208
|
// Google Fonts (automatic)
|
|
168
209
|
{ name: 'Roboto', weight: 400 },
|
|
@@ -183,11 +224,8 @@ Choose from multiple emoji providers:
|
|
|
183
224
|
|
|
184
225
|
```typescript
|
|
185
226
|
const template = defineTemplate({
|
|
186
|
-
id: 'emoji-template',
|
|
187
|
-
name: 'Emoji Template',
|
|
188
|
-
description: 'Template with emoji support',
|
|
189
227
|
fonts: [{ name: 'Inter', weight: 400 }],
|
|
190
|
-
emojiProvider: 'twemoji', //
|
|
228
|
+
emojiProvider: 'twemoji', // 'fluent' | 'fluentFlat' | 'noto' | 'blobmoji' | 'openmoji'
|
|
191
229
|
renderer: ({ params }) => `
|
|
192
230
|
<div>
|
|
193
231
|
<h1>${params.title}</h1>
|
|
@@ -202,8 +240,8 @@ const template = defineTemplate({
|
|
|
202
240
|
Add custom logic before and after rendering:
|
|
203
241
|
|
|
204
242
|
```typescript
|
|
205
|
-
const renderer =
|
|
206
|
-
templates:
|
|
243
|
+
const renderer = createRenderer({
|
|
244
|
+
templates: { 'blog-post': blogTemplate },
|
|
207
245
|
beforeRender: async (templateId, params) => {
|
|
208
246
|
console.log(`Rendering ${templateId}`, params);
|
|
209
247
|
// Log analytics, validate params, etc.
|
|
@@ -215,16 +253,49 @@ const renderer = createTemplateRenderer({
|
|
|
215
253
|
});
|
|
216
254
|
```
|
|
217
255
|
|
|
256
|
+
### Caching Configuration
|
|
257
|
+
|
|
258
|
+
Configure caching strategy for fonts and emojis/icons:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// Memory Cache (default)
|
|
262
|
+
const memoryRenderer = createRenderer({
|
|
263
|
+
templates: { 'blog-post': blogTemplate },
|
|
264
|
+
cache: {
|
|
265
|
+
type: 'memory',
|
|
266
|
+
ttl: 3600000, // 1 hour
|
|
267
|
+
max: 100 // max items
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Filesystem Cache
|
|
272
|
+
const fsRenderer = createRenderer({
|
|
273
|
+
templates: { 'blog-post': blogTemplate },
|
|
274
|
+
cache: {
|
|
275
|
+
type: 'filesystem',
|
|
276
|
+
dir: './.ogify-cache', // cache directory
|
|
277
|
+
ttl: 3600000, // 1 hour
|
|
278
|
+
max: 100 // max items
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
218
283
|
### Platform-Specific Dimensions
|
|
219
284
|
|
|
220
285
|
Generate images for different platforms:
|
|
221
286
|
|
|
222
287
|
```typescript
|
|
223
288
|
// Facebook/LinkedIn (1200x630)
|
|
224
|
-
const facebookImage = await renderer.renderToImage('
|
|
289
|
+
const facebookImage = await renderer.renderToImage('basic', {
|
|
290
|
+
title: 'Hello World',
|
|
291
|
+
subtitle: 'My first OG image',
|
|
292
|
+
});
|
|
225
293
|
|
|
226
294
|
// Twitter (1200x675)
|
|
227
|
-
const twitterImage = await renderer.renderToImage('
|
|
295
|
+
const twitterImage = await renderer.renderToImage('basic', {
|
|
296
|
+
title: 'Hello World',
|
|
297
|
+
subtitle: 'My first OG image',
|
|
298
|
+
}, {
|
|
228
299
|
width: 1200,
|
|
229
300
|
height: 675
|
|
230
301
|
});
|
|
@@ -234,7 +305,7 @@ const twitterImage = await renderer.renderToImage('blog-post', params, {
|
|
|
234
305
|
|
|
235
306
|
### Supported CSS Properties
|
|
236
307
|
|
|
237
|
-
Templates support a subset of CSS properties via Satori
|
|
308
|
+
Templates support a subset of CSS properties via Satori. See [Satori CSS](https://github.com/vercel/satori?tab=readme-ov-file#css) for more details.
|
|
238
309
|
|
|
239
310
|
- **Layout**: `display: flex`, `flexDirection`, `alignItems`, `justifyContent`
|
|
240
311
|
- **Spacing**: `padding`, `margin`, `gap`
|
|
@@ -242,6 +313,7 @@ Templates support a subset of CSS properties via Satori:
|
|
|
242
313
|
- **Background**: `backgroundColor`, `backgroundImage`
|
|
243
314
|
- **Border**: `border`, `borderRadius`
|
|
244
315
|
- **Size**: `width`, `height`, `maxWidth`, `maxHeight`
|
|
316
|
+
- ...
|
|
245
317
|
|
|
246
318
|
### Tailwind-like Utilities
|
|
247
319
|
|
|
@@ -264,23 +336,21 @@ Defines a new OG template.
|
|
|
264
336
|
|
|
265
337
|
**Parameters:**
|
|
266
338
|
|
|
267
|
-
- `id` (string): Unique identifier
|
|
268
|
-
- `name` (string): Human-readable name
|
|
269
|
-
- `description` (string): Template description
|
|
270
339
|
- `renderer` (function): Function that returns HTML string
|
|
271
340
|
- `fonts` (array): Array of font configurations
|
|
272
341
|
- `emojiProvider` (optional): Emoji provider to use
|
|
273
342
|
|
|
274
343
|
**Returns:** `OgTemplate`
|
|
275
344
|
|
|
276
|
-
### `
|
|
345
|
+
### `createRenderer(config)`
|
|
277
346
|
|
|
278
347
|
Creates a new template renderer instance.
|
|
279
348
|
|
|
280
349
|
**Parameters:**
|
|
281
350
|
|
|
282
|
-
- `templates` (
|
|
283
|
-
- `
|
|
351
|
+
- `templates` (object): Map of template definitions keyed by ID
|
|
352
|
+
- `sharedParams` (optional): Default parameters for all templates
|
|
353
|
+
- `cache` (optional): Cache configuration object
|
|
284
354
|
- `beforeRender` (optional): Hook called before rendering
|
|
285
355
|
- `afterRender` (optional): Hook called after rendering
|
|
286
356
|
|
|
@@ -293,40 +363,48 @@ Renders a template to a PNG buffer.
|
|
|
293
363
|
**Parameters:**
|
|
294
364
|
|
|
295
365
|
- `templateId` (string): ID of the template to render
|
|
296
|
-
- `params` (object): Parameters to pass to the template
|
|
366
|
+
- `params` (object | function): Parameters (or function returning params) to pass to the template
|
|
297
367
|
- `options` (optional): Rendering options
|
|
298
368
|
- `width` (number): Image width (default: 1200)
|
|
299
369
|
- `height` (number): Image height (default: 630)
|
|
370
|
+
- `isRTL` (boolean): Enable Right-to-Left text direction (default: false)
|
|
300
371
|
|
|
301
372
|
**Returns:** `Promise<Buffer>`
|
|
302
373
|
|
|
303
|
-
###
|
|
374
|
+
### RTL Support
|
|
304
375
|
|
|
305
|
-
|
|
376
|
+
OGify supports Right-to-Left (RTL) languages via the `isRTL` option.
|
|
306
377
|
|
|
307
|
-
|
|
378
|
+
```typescript
|
|
379
|
+
const image = await renderer.renderToImage('basic', {
|
|
380
|
+
title: 'مرحبا بالعالم', // Arabic: Hello World
|
|
381
|
+
subtitle: 'هذه أول صورة OG لي',
|
|
382
|
+
}, {
|
|
383
|
+
isRTL: true
|
|
384
|
+
});
|
|
385
|
+
```
|
|
308
386
|
|
|
309
|
-
### `renderer.
|
|
387
|
+
### `renderer.getTemplate(id)`
|
|
310
388
|
|
|
311
|
-
|
|
389
|
+
Retrieves a template by ID.
|
|
312
390
|
|
|
313
|
-
**Returns:** `
|
|
391
|
+
**Returns:** `OgTemplate | undefined`
|
|
314
392
|
|
|
315
393
|
## ⚡ Performance & Production
|
|
316
394
|
|
|
317
395
|
### **Automatic Caching (Zero Config)**
|
|
318
396
|
|
|
319
|
-
OGify automatically caches fonts and
|
|
397
|
+
OGify automatically caches fonts, emojis, and generated images in memory - no configuration required
|
|
320
398
|
|
|
321
399
|
```typescript
|
|
322
400
|
// First render: Downloads fonts from Google Fonts (~500ms)
|
|
323
|
-
await renderer.renderToImage('
|
|
401
|
+
await renderer.renderToImage('basic', { title: 'Post 1', subtitle: '...' });
|
|
324
402
|
|
|
325
403
|
// Second render: Uses cached fonts (~50ms) - 10x faster! ⚡
|
|
326
|
-
await renderer.renderToImage('
|
|
404
|
+
await renderer.renderToImage('basic', { title: 'Post 2', subtitle: '...' });
|
|
327
405
|
|
|
328
406
|
// Third+ renders: Lightning fast from cache
|
|
329
|
-
await renderer.renderToImage('
|
|
407
|
+
await renderer.renderToImage('basic', { title: 'Post 3', subtitle: '...' });
|
|
330
408
|
```
|
|
331
409
|
|
|
332
410
|
## 📋 Changelog
|
|
@@ -347,4 +425,5 @@ Built on top of:
|
|
|
347
425
|
|
|
348
426
|
- [satori](https://github.com/vercel/satori) - SVG generation
|
|
349
427
|
- [satori-html](https://github.com/vercel/satori-html) - HTML to VDOM conversion
|
|
350
|
-
- [resvg-js](https://github.com/thx/resvg-js) - PNG conversion
|
|
428
|
+
- [@resvg/resvg-js](https://github.com/thx/resvg-js) - SVG to PNG conversion
|
|
429
|
+
- [lru-cache](https://github.com/isaacs/node-lru-cache) - LRU cache
|