Skip to content

Added satori and improved react email #1877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,15 @@
"guides/examples/open-ai-with-retrying",
"guides/examples/pdf-to-image",
"guides/examples/puppeteer",
"guides/examples/react-pdf",
"guides/examples/react-email",
"guides/examples/resend-email-sequence",
"guides/examples/satori",
"guides/examples/scrape-hacker-news",
"guides/examples/sentry-error-tracking",
"guides/examples/sharp-image-processing",
"guides/examples/supabase-database-operations",
"guides/examples/supabase-storage-upload",
"guides/examples/react-pdf",
"guides/examples/react-email",
"guides/examples/resend-email-sequence",
"guides/examples/vercel-ai-sdk",
"guides/examples/vercel-sync-env-vars"
]
Expand Down
187 changes: 184 additions & 3 deletions docs/guides/examples/react-email.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ description: "Learn how to send beautiful emails using React Email and Trigger.d

## Overview

This example demonstrates how to use Trigger.dev to send emails using React Email.
This example demonstrates how to use Trigger.dev to send emails using [React Email](https://react.email/).

<Note>
This example uses [Resend](https://resend.com) as the email provider. You can use other email
providers like [SendGrid](https://sendgrid.com) or [Loops](https://loops.so) as well. Full list of
providers like [Loops](https://loops.so) or [SendGrid](https://sendgrid.com) etc. Full list of
their integrations can be found [here](https://react.email/docs/introduction#integrations).
</Note>

Expand Down Expand Up @@ -98,7 +98,7 @@ export const sendEmail = task({
});
```

## How the email should look
## The email

This example email should look like this:
![React Email](/images/react-email.png)
Expand All @@ -121,3 +121,184 @@ To test this task in the [dashboard](https://cloud.trigger.dev), you can use the
## Deploying your task

Deploy the task to production using the Trigger.dev CLI `deploy` command.

## Using Cursor / AI to build your emails

In this video you can see how we use Cursor to build a welcome email.

We recommend using our [Cursor rules](https://trigger.dev/changelog/cursor-rules-writing-tasks/) to help you build your tasks and emails.

#### Video: creating a new email template using Cursor

<video
src="https://content.trigger.dev/trigger-welcome-email-cursor.mp4"
controls
muted
autoPlay
loop
/>

#### The generated email template

![Cursor](/images/react-email-welcome.png)

#### The generated code

```tsx trigger/welcome.tsx
import {
Body,
Button,reac
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Section,
Text,
} from "@react-email/components";

const baseUrl = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "";

export const TriggerDevWelcomeEmail = () => (
<Html>
<Head />
<Preview>Welcome to Trigger.dev - Your background jobs platform!</Preview>
<Body style={main}>
<Container style={container}>
<Section style={box}>
<Img
src="https://trigger.dev/assets/triggerdev-lockup--light.svg"
width="150"
height="40"
alt="Trigger.dev"
/>
<Hr style={hr} />
<Text style={paragraph}>
Thanks for signing up for Trigger.dev! You're now ready to start
creating powerful background jobs and workflows.
</Text>
<Text style={paragraph}>
You can monitor your jobs, view runs, and manage your projects right
from your dashboard.
</Text>
<Button style={button} href="https://cloud.trigger.dev/dashboard">
View your Trigger.dev Dashboard
</Button>
<Hr style={hr} />
<Text style={paragraph}>
To help you get started, check out our{" "}
<Link style={anchor} href="https://trigger.dev/docs">
documentation
</Link>{" "}
and{" "}
<Link style={anchor} href="https://trigger.dev/docs/quickstart">
quickstart guide
</Link>
.
</Text>
<Text style={paragraph}>
You can create your first job using our SDK, set up integrations,
and configure triggers to automate your workflows. Take a look at
our{" "}
<Link style={anchor} href="https://trigger.dev/docs/examples">
examples
</Link>{" "}
for inspiration.
</Text>
<Text style={paragraph}>
Join our{" "}
<Link style={anchor} href="https://discord.gg/kA47vcd8Qr">
Discord community
</Link>{" "}
to connect with other developers and get help when you need it.
</Text>
<Text style={paragraph}>
We're here to help you build amazing things. If you have any
questions, check out our{" "}
<Link style={anchor} href="https://trigger.dev/docs">
documentation
</Link>{" "}
or reach out to us on Discord.
</Text>
<Text style={paragraph}>— The Trigger.dev team</Text>
<Hr style={hr} />
<Text style={footer}>Trigger.dev Inc.</Text>
</Section>
</Container>
</Body>
</Html>
);

export default TriggerDevWelcomeEmail;

const main = {
backgroundColor: "#0E0C15",
fontFamily:
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
};

const container = {
backgroundColor: "#1D1B27",
margin: "0 auto",
padding: "20px 0 48px",
marginBottom: "64px",
};

const box = {
padding: "0 48px",
};

const hr = {
borderColor: "#2D2B3B",
margin: "20px 0",
};

const paragraph = {
color: "#E1E1E3",
fontSize: "16px",
lineHeight: "24px",
textAlign: "left" as const,
};

const anchor = {
color: "#A78BFA",
};

const button = {
backgroundColor: "#7C3AED",
borderRadius: "6px",
color: "#fff",
fontSize: "16px",
fontWeight: "bold",
textDecoration: "none",
textAlign: "center" as const,
display: "block",
width: "100%",
padding: "12px",
};

const footer = {
color: "#9CA3AF",
fontSize: "12px",
lineHeight: "16px",
};
```

## Learn more

### React Email docs

Check out the [React Email docs](https://react.email/docs) and learn how to set up and use React Email, including how to preview your emails locally.

<CardGroup cols={2}>
<Card title="Components" icon="puzzle-piece" href="https://react.email/components">
Pre-built components you can copy and paste into your emails.
</Card>
<Card title="Templates" icon="rectangle-list" href="https://react.email/templates">
Extensive pre-built templates ready to use.
</Card>
</CardGroup>
2 changes: 1 addition & 1 deletion docs/guides/examples/react-pdf.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: "This example will show you how to generate a PDF using Trigger.dev

## Overview

This example demonstrates how to use Trigger.dev to generate a PDF using `react-pdf` and save it to Cloudflare R2.
This example demonstrates how to use Trigger.dev to generate a PDF using [react-pdf](https://react-pdf.org/) and save it to Cloudflare R2.

## Task code

Expand Down
133 changes: 133 additions & 0 deletions docs/guides/examples/satori.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
title: "Generate OG Images using Satori"
sidebarTitle: "Satori OG Images"
description: "Learn how to generate dynamic Open Graph images using Satori and Trigger.dev."
---

## Overview

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.

This can be customized and extended however you like, full list of options can be found [here](https://github.com/vercel/satori).

## Task code

```tsx trigger/generateOgImage.ts
import { schemaTask } from "@trigger.dev/sdk/v3";
import { z } from "zod";
import satori from "satori";
import sharp from "sharp";
import { join } from "path";
import fs from "fs/promises";

export const generateOgImage = schemaTask({
id: "generate-og-image",
schema: z.object({
width: z.number().optional(),
height: z.number().optional(),
title: z.string(),
imageUrl: z.string().url(),
}),
run: async (payload) => {
// Load font
const fontResponse = await fetch(
"https://github.com/googlefonts/roboto/raw/main/src/hinted/Roboto-Regular.ttf"
).then((res) => res.arrayBuffer());

// Fetch and convert image to base64
const imageResponse = await fetch(payload.imageUrl);
const imageBuffer = await imageResponse.arrayBuffer();
const imageBase64 = `data:${
imageResponse.headers.get("content-type") || "image/jpeg"
};base64,${Buffer.from(imageBuffer).toString("base64")}`;

const markup = (
<div
style={{
width: payload.width ?? 1200,
height: payload.height ?? 630,
display: "flex",
backgroundColor: "#121317",
position: "relative",
fontFamily: "Roboto",
}}
>
<img
src={imageBase64}
width={payload.width ?? 1200}
height={payload.height ?? 630}
style={{
objectFit: "cover",
}}
/>
<h1
style={{
fontSize: "60px",
fontWeight: "bold",
color: "#fff",
margin: 0,
position: "absolute",
top: "50%",
transform: "translateY(-50%)",
left: "48px",
maxWidth: "60%",
textShadow: "0 2px 4px rgba(0,0,0,0.5)",
}}
>
{payload.title}
</h1>
</div>
);

const svg = await satori(markup, {
width: payload.width ?? 1200,
height: payload.height ?? 630,
fonts: [
{
name: "Roboto",
data: fontResponse,
weight: 400,
style: "normal",
},
],
});

const fileName = `og-${Date.now()}.jpg`;
const tempDir = join(process.cwd(), "tmp");
await fs.mkdir(tempDir, { recursive: true });
const outputPath = join(tempDir, fileName);

await sharp(Buffer.from(svg))
.jpeg({
quality: 90,
mozjpeg: true,
})
.toFile(outputPath);

return {
filePath: outputPath,
width: payload.width,
height: payload.height,
};
},
});
```

## Image example

This image was generated using the above task.

![OG Image](/images/react-satori-og.jpg)

## Testing your task

To test this task in the [dashboard](https://cloud.trigger.dev), you can use the following payload:

```json
{
"title": "My Awesome OG image",
"imageUrl": "<your-image-url>",
"width": 1200, // optional, defaults to 1200
"height": 630 // optional, defaults to 630
}
```
4 changes: 4 additions & 0 deletions docs/guides/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Task code you can copy and paste to use in your project. They can all be extende
| [Vercel AI SDK](/guides/examples/vercel-ai-sdk) | Use Vercel AI SDK to generate text using OpenAI. |
| [Vercel sync environment variables](/guides/examples/vercel-sync-env-vars) | Automatically sync environment variables from your Vercel projects to Trigger.dev. |

## Cursor rules

If you use Cursor, we have a set of Cursor rules which will help you build your tasks. You can find our installation guide [here](https://trigger.dev/changelog/cursor-rules-writing-tasks/).

<Note>
If you would like to see a guide for your framework, or an example task for your use case, please
request it in our [Discord server](https://trigger.dev/discord) and we'll add it to the list.
Expand Down
Binary file added docs/images/react-email-welcome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/react-satori-og.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.