Skip to content

Send email from Next.js

This guide uses the App Router (Next.js 14 and later). Email is sent from the server only: your API key never reaches the browser.

  1. Install the SDK

    Terminal window
    npm install vaartix

    Add your key to .env.local (and to your host’s environment variables when you deploy):

    .env.local
    VAARTIX_API_KEY=vx_live_...
  2. Create a shared client

    lib/vaartix.ts
    import { Vaartix } from 'vaartix';
    // Reads VAARTIX_API_KEY from the environment.
    export const vaartix = new Vaartix();
  3. Send from a Server Action

    app/contact/actions.ts
    'use server';
    import { vaartix } from '@/lib/vaartix';
    export async function sendWelcome(formData: FormData) {
    const email = String(formData.get('email'));
    const { data, error } = await vaartix.emails.send({
    from: 'Acme <[email protected]>',
    to: email,
    subject: 'Welcome to Acme',
    html: '<p>Thanks for signing up!</p>',
    });
    if (error) return { ok: false, message: error.message };
    return { ok: true, id: data.id };
    }

    Or from a Route Handler, if another service calls you:

    app/api/send/route.ts
    import { vaartix } from '@/lib/vaartix';
    export async function POST(request: Request) {
    const { to } = await request.json();
    const { data, error } = await vaartix.emails.send({
    from: 'Acme <[email protected]>',
    to,
    subject: 'Your report is ready',
    text: 'Download it from your dashboard.',
    });
    if (error) return Response.json({ error }, { status: error.statusCode ?? 500 });
    return Response.json(data);
    }

Until your domain is verified, send from @test.vaartix.com to [email protected]: see Test mode.

React Email lets you write emails as React components. Install it, write a template, and pass it as react:

Terminal window
npm install @react-email/components @react-email/render
emails/welcome.tsx
import { Body, Button, Container, Heading, Html, Text } from '@react-email/components';
export function WelcomeEmail({ name }: { name: string }) {
return (
<Html>
<Body style={{ fontFamily: 'sans-serif' }}>
<Container>
<Heading>Welcome, {name}</Heading>
<Text>Your account is ready.</Text>
<Button href="https://acme.in/dashboard">Open your dashboard</Button>
</Container>
</Body>
</Html>
);
}
app/signup/actions.ts
'use server';
import { vaartix } from '@/lib/vaartix';
import { WelcomeEmail } from '@/emails/welcome';
export async function onSignup(user: { name: string; email: string }) {
await vaartix.emails.send(
{
from: 'Acme <[email protected]>',
to: user.email,
subject: 'Welcome to Acme',
react: <WelcomeEmail name={user.name} />,
},
// Safe to retry: the same key never sends twice within 24 hours.
{ idempotencyKey: `welcome-${user.email}` },
);
}

The SDK renders the HTML and a plain-text version for you.

Verify each webhook against the raw body, so read it with request.text() before parsing:

app/api/webhooks/vaartix/route.ts
import { verifyWebhook, WebhookVerificationError } from 'vaartix';
export async function POST(request: Request) {
const payload = await request.text();
try {
const event = verifyWebhook({
payload,
headers: request.headers,
secret: process.env.VAARTIX_WEBHOOK_SECRET!,
});
if (event.type === 'email.bounced') {
// Stop emailing event.data.recipient
}
return new Response(null, { status: 200 });
} catch (err) {
if (err instanceof WebhookVerificationError) return new Response(null, { status: 400 });
throw err;
}
}

Return a 2xx quickly and do slow work afterwards; Vaartix retries anything else. See Set up webhooks.