Farm.js

Resend Integration

Render React Email templates, send with Resend, schedule messages, preview templates, and receive webhooks.

Define templates

src/lib/email.ts
import { resend, template } from "@farm.js/email";

const templates = {
  welcome: template({
    subject: "Welcome to Farm",
    component: ({ name }: { name: string }) => <p>Hello {name}</p>,
  }),
};

export const email = resend({
  apiKey: process.env.RESEND_API_KEY,
  defaults: { from: "hello@example.com" },
  templates,
});

Choose SDK ownership

Let Farm construct Resend

The example above is the default path. When instance is omitted, Farm creates the Resend SDK from apiKey, supplied directly or through RESEND_API_KEY.

Provide an application-owned instance

import { Resend } from "resend";
import { resend } from "@farm.js/email";

const resendClient = new Resend(process.env.RESEND_API_KEY);

export const email = resend({
  instance: resendClient,
  defaults: { from: "hello@example.com" },
  templates,
});

The instance wins if an API key is also supplied. Templates, defaults, scheduling, previews, routes, and webhooks remain integration options in either mode.

Send mail

Caller
await apiClient.email.send.post({
  body: {
    template: "welcome",
    to: "ada@example.com",
    data: { name: "Ada" },
  },
});

What Resend adds

AreaDetails
TemplatesTyped React Email templates with subjects, preview text, defaults, and preview props.
SendA typed send route for transactional messages.
ScheduleA typed route for future delivery.
PreviewHTML and text rendering for local previews or admin tools.
Templates indexA route that lists configured templates and preview metadata.
WebhooksOptional Resend webhook receivers for delivery events.

Preview before sending

const preview = await api.email.preview.post({
  body: {
    templateId: "welcome",
    data: {
      name: "Ada",
    },
  },
});

console.log(preview.data?.subject);
console.log(preview.data?.html);

Schedule mail

await api.email.schedule.post({
  body: {
    templateId: "welcome",
    to: "ada@example.com",
    when: "tomorrow 9am",
    data: {
      name: "Ada",
    },
  },
});

Template defaults

Templates can supply their own subject, previewText, from, and replyTo. The integration also supports global defaults, which keeps common sender details out of every call.

const templates = {
  invite: template({
    subject: ({ workspace }: { workspace: string }) => `Join ${workspace}`,
    previewText: "You were invited to collaborate.",
    component: ({ workspace }: { workspace: string }) => <p>Join {workspace}</p>,
  }),
};

Production notes

  • Set RESEND_API_KEY and a verified RESEND_FROM_EMAIL.
  • Use idempotency keys for important transactional flows.
  • Preview templates in development before sending them to real recipients.
  • Keep template IDs stable because they are part of the caller contract.
  • Verify webhooks before using delivery events for product logic.