Back to Blog

How to Send Emails from Angular (2026 Guide)

10 min read

Angular runs in the browser. Like React, you can't send emails directly from the client. You call a backend API that holds your API key and sends the email server-side.

This guide covers the Angular side (services, forms, HttpClient) and the minimal backend needed.

The Pattern

Angular Form → HttpClient.post("/api/send-email") → Your Backend → Email Provider

Email Service

// src/app/services/email.service.ts
import { Injectable, inject } from "@angular/core";
import { HttpClient } from "@angular/common/http";
 
interface SendEmailRequest {
  email: string;
  message: string;
}
 
@Injectable({ providedIn: "root" })
export class EmailService {
  private http = inject(HttpClient);
 
  sendContactEmail(data: SendEmailRequest) {
    return this.http.post<{ sent: boolean }>("/api/send-email", data);
  }
 
  sendWelcomeEmail(email: string, name: string) {
    return this.http.post<{ jobId: string }>("/api/send-welcome", { email, name });
  }
}

Contact Form Component

// src/app/components/contact-form.component.ts
import { Component, inject } from "@angular/core";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import { EmailService } from "../services/email.service";
 
@Component({
  selector: "app-contact-form",
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <input formControlName="email" type="email" placeholder="Your email" />
      <textarea formControlName="message" placeholder="Message"></textarea>
      <button type="submit" [disabled]="sending">
        {{ sending ? "Sending..." : "Send" }}
      </button>
      @if (status === "sent") {
        <p style="color: green">Message sent!</p>
      }
      @if (status === "error") {
        <p style="color: red">Failed to send. Try again.</p>
      }
    </form>
  `,
})
export class ContactFormComponent {
  private emailService = inject(EmailService);
  private fb = inject(FormBuilder);
 
  form = this.fb.group({
    email: ["", [Validators.required, Validators.email]],
    message: ["", Validators.required],
  });
 
  sending = false;
  status: "idle" | "sent" | "error" = "idle";
 
  onSubmit() {
    if (this.form.invalid) return;
 
    this.sending = true;
    this.status = "idle";
 
    this.emailService
      .sendContactEmail(this.form.value as { email: string; message: string })
      .subscribe({
        next: () => {
          this.status = "sent";
          this.sending = false;
          this.form.reset();
        },
        error: () => {
          this.status = "error";
          this.sending = false;
        },
      });
  }
}

Backend API

server/index.ts
import express from "express";
import Sequenzy from "sequenzy";

const app = express();
const sequenzy = new Sequenzy();

app.use(express.json());

app.post("/api/send-email", async (req, res) => {
const { email, message } = req.body;

if (!email || !message) {
  return res.status(400).json({ error: "email and message required" });
}

try {
  await sequenzy.transactional.send({
    to: "you@yourcompany.com",
    subject: `Contact from ${email}`,
    body: `<p><strong>From:</strong> ${email}</p><p>${message}</p>`,
  });

  res.json({ sent: true });
} catch {
  res.status(500).json({ error: "Failed to send" });
}
});

app.listen(3000);
server/index.ts
import express from "express";
import { Resend } from "resend";

const app = express();
const resend = new Resend(process.env.RESEND_API_KEY);

app.use(express.json());

app.post("/api/send-email", async (req, res) => {
const { email, message } = req.body;

if (!email || !message) {
  return res.status(400).json({ error: "email and message required" });
}

const { error } = await resend.emails.send({
  from: "Contact <noreply@yourdomain.com>",
  to: "you@yourcompany.com",
  subject: `Contact from ${email}`,
  html: `<p><strong>From:</strong> ${email}</p><p>${message}</p>`,
});

if (error) return res.status(500).json({ error: "Failed to send" });
res.json({ sent: true });
});

app.listen(3000);
server/index.ts
import express from "express";
import sgMail from "@sendgrid/mail";

sgMail.setApiKey(process.env.SENDGRID_API_KEY!);

const app = express();
app.use(express.json());

app.post("/api/send-email", async (req, res) => {
const { email, message } = req.body;

if (!email || !message) {
  return res.status(400).json({ error: "email and message required" });
}

try {
  await sgMail.send({
    to: "you@yourcompany.com",
    from: "noreply@yourdomain.com",
    subject: `Contact from ${email}`,
    html: `<p><strong>From:</strong> ${email}</p><p>${message}</p>`,
  });

  res.json({ sent: true });
} catch {
  res.status(500).json({ error: "Failed to send" });
}
});

app.listen(3000);

Proxy in Development

Add a proxy to angular.json so Angular dev server forwards API requests:

// proxy.conf.json
{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}
ng serve --proxy-config proxy.conf.json

Going to Production

1. Never Expose API Keys

Keep API keys on the server. Never put them in environment.ts.

2. Use Angular SSR

With Angular SSR, you can make email API calls directly in server-side code without a separate backend.

3. Verify Your Domain

Add SPF, DKIM, DMARC DNS records.

Beyond Transactional

Sequenzy handles transactional sends, marketing campaigns, automated sequences, and subscriber management from one API. Native Stripe integration for SaaS.

Wrapping Up

  1. Email service with HttpClient for API calls
  2. Reactive forms with validation and loading states
  3. Backend API to keep keys secure
  4. Dev proxy for local development

Pick your provider, build the backend, and connect your Angular forms.