Skip to content

Commit 2b72254

Browse files
committed
Added satori example
1 parent b047b3b commit 2b72254

File tree

3 files changed

+137
-3
lines changed

3 files changed

+137
-3
lines changed

docs/docs.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,14 +332,15 @@
332332
"guides/examples/open-ai-with-retrying",
333333
"guides/examples/pdf-to-image",
334334
"guides/examples/puppeteer",
335+
"guides/examples/react-pdf",
336+
"guides/examples/react-email",
337+
"guides/examples/resend-email-sequence",
338+
"guides/examples/satori",
335339
"guides/examples/scrape-hacker-news",
336340
"guides/examples/sentry-error-tracking",
337341
"guides/examples/sharp-image-processing",
338342
"guides/examples/supabase-database-operations",
339343
"guides/examples/supabase-storage-upload",
340-
"guides/examples/react-pdf",
341-
"guides/examples/react-email",
342-
"guides/examples/resend-email-sequence",
343344
"guides/examples/vercel-ai-sdk",
344345
"guides/examples/vercel-sync-env-vars"
345346
]

docs/guides/examples/satori.mdx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
title: "Generate OG Images using Satori"
3+
sidebarTitle: "Satori OG Images"
4+
description: "Learn how to generate dynamic Open Graph images using Satori and Trigger.dev."
5+
---
6+
7+
## Overview
8+
9+
This example demonstrates how to use Trigger.dev to generate dynamic Open Graph (OG) images using Vercel's [Satori](https://github.com/vercel/satori). The task takes a title and image URL as input and generates a beautiful OG image with text overlay.
10+
11+
This can be customized and extended however you like, full list of options can be found [here](https://github.com/vercel/satori).
12+
13+
## Task code
14+
15+
```tsx trigger/generateOgImage.ts
16+
import { schemaTask } from "@trigger.dev/sdk/v3";
17+
import { z } from "zod";
18+
import satori from "satori";
19+
import sharp from "sharp";
20+
import { join } from "path";
21+
import fs from "fs/promises";
22+
23+
export const generateOgImage = schemaTask({
24+
id: "generate-og-image",
25+
schema: z.object({
26+
width: z.number().optional(),
27+
height: z.number().optional(),
28+
title: z.string(),
29+
imageUrl: z.string().url(),
30+
}),
31+
run: async (payload) => {
32+
// Load font
33+
const fontResponse = await fetch(
34+
"https://github.com/googlefonts/roboto/raw/main/src/hinted/Roboto-Regular.ttf"
35+
).then((res) => res.arrayBuffer());
36+
37+
// Fetch and convert image to base64
38+
const imageResponse = await fetch(payload.imageUrl);
39+
const imageBuffer = await imageResponse.arrayBuffer();
40+
const imageBase64 = `data:${
41+
imageResponse.headers.get("content-type") || "image/jpeg"
42+
};base64,${Buffer.from(imageBuffer).toString("base64")}`;
43+
44+
const markup = (
45+
<div
46+
style={{
47+
width: payload.width ?? 1200,
48+
height: payload.height ?? 630,
49+
display: "flex",
50+
backgroundColor: "#121317",
51+
position: "relative",
52+
fontFamily: "Roboto",
53+
}}
54+
>
55+
<img
56+
src={imageBase64}
57+
width={payload.width ?? 1200}
58+
height={payload.height ?? 630}
59+
style={{
60+
objectFit: "cover",
61+
}}
62+
/>
63+
<h1
64+
style={{
65+
fontSize: "60px",
66+
fontWeight: "bold",
67+
color: "#fff",
68+
margin: 0,
69+
position: "absolute",
70+
top: "50%",
71+
transform: "translateY(-50%)",
72+
left: "48px",
73+
maxWidth: "60%",
74+
textShadow: "0 2px 4px rgba(0,0,0,0.5)",
75+
}}
76+
>
77+
{payload.title}
78+
</h1>
79+
</div>
80+
);
81+
82+
const svg = await satori(markup, {
83+
width: payload.width ?? 1200,
84+
height: payload.height ?? 630,
85+
fonts: [
86+
{
87+
name: "Roboto",
88+
data: fontResponse,
89+
weight: 400,
90+
style: "normal",
91+
},
92+
],
93+
});
94+
95+
const fileName = `og-${Date.now()}.jpg`;
96+
const tempDir = join(process.cwd(), "tmp");
97+
await fs.mkdir(tempDir, { recursive: true });
98+
const outputPath = join(tempDir, fileName);
99+
100+
await sharp(Buffer.from(svg))
101+
.jpeg({
102+
quality: 90,
103+
mozjpeg: true,
104+
})
105+
.toFile(outputPath);
106+
107+
return {
108+
filePath: outputPath,
109+
width: payload.width,
110+
height: payload.height,
111+
};
112+
},
113+
});
114+
```
115+
116+
## Image example
117+
118+
This image was generated using the above task.
119+
120+
![OG Image](/images/react-satori-og.jpg)
121+
122+
## Testing your task
123+
124+
To test this task in the [dashboard](https://cloud.trigger.dev), you can use the following payload:
125+
126+
```json
127+
{
128+
"title": "My Awesome OG image",
129+
"imageUrl": "<your-image-url>",
130+
"width": 1200, // optional, defaults to 1200
131+
"height": 630 // optional, defaults to 630
132+
}
133+
```

docs/images/react-satori-og.jpg

59.9 KB
Loading

0 commit comments

Comments
 (0)