Repository navigation
Dynamic image generation for React Email #3635
Replies: 1 comment
|
I will share my thoughts here and I hope it helps with something :) HTML-based visuals still have the advantage that they don't require external hosting and remain visible when email clients block images. They can be reasonable for simple linear barcodes. However, they do not generalize well to QR codes, charts, typography, or more complex visuals. Even though an image-based API has infra costs (it's also more complex), it provides a reusable primitive and more predictable rendering across email clients. Rough sketch:
import { renderImage } from '@react-email/image/node';
const image = await renderImage(<RevenueChart data={data} />, {
width: 600,
height: 240,
fonts: [inter],
});It could return the image bytes alongside the information required to publish and render it - something like this: interface RenderedImage {
data: Uint8Array;
contentType: 'image/png';
width: number;
height: number;
digest: string;
}
interface ImagePublisher {
publish(image: RenderedImage): Promise<{
url: string;
}>;
}Users could implement it with S3, Vercel Blob, their own CDN, etc. Resend could also ship an official resendPublisher as a convenience, though it wouldn't be required. const image = await renderImage(<RevenueChart data={data} />, {
width: 600,
height: 240,
});
const { url } = await publisher.publish(image);The publisher would be responsible for returning a URL. It should also be idempotent by This is especially important for batch sending, where the same email could be rendered for thousands of recipients. The URL can then be used with the existing React Email <Img
src={url}
width="600"
height="240"
alt="A cool image!"
/>
import { DynamicImage } from '@react-email/image/server';
export async function Email() {
return (
<DynamicImage
publisher={s3Publisher}
width={600}
height={240}
alt="Revenue increased from $18k to $31k"
>
<RevenueChart data={data} />
</DynamicImage>
);
}Its implementation would be roughly like this: async function DynamicImage({
children,
publisher,
alt,
width,
height,
}) {
const image = await renderImage(children, {
width,
height,
});
const { url } = await publisher.publish(image);
return (
<Img
src={url}
width={width}
height={height}
alt={alt}
/>
);
}
This means the same component could work when rendering an email from plain Node or from within an RSC application, without depending on Next.js or RSC-specific APIs. The package boundaries could make the runtime requirements explicit: |
Uh oh!
There was an error while loading. Please reload this page.
Goals
Explore APIs dynamic image generation for React Email
Background
The idea
A first-class way to render dynamic visuals in React Email as images. If we
can render JSX/HTML+CSS to an image, one primitive covers barcodes, QR codes,
charts, and more. Likely path:
satoriforHTML/CSS → SVG, then SVG → PNG (SVG has poor email-client support).
The hard part: the API
Rendering is the easy part. The real challenge is how the user gets the image:
cleanly is the crux.
Questions
We'll likely close #2960 in favor of settling this first.
Proposal
We don't have a proposal yet, we will use this discussion to explore.
All reactions