Firebase email guides

Firebase Trigger Email extension alternative

Firebase has an official Trigger Email extension for sending emails from Firestore.

It is a useful Firebase-native option, especially if you like the idea of writing email requests into a Firestore collection and letting an extension process them.

But it is important to understand what the extension is and what it is not.

Firebase Trigger Email is best thought of as a Firestore-to-SMTP bridge.

It helps trigger email delivery from Firebase, but it does not remove all the work involved in running transactional email for an app.

If you want a Firebase-native way to send your own email documents, it can be a good fit.

If you want common app emails already packaged as templates, a narrower template-first product like EmailsDone may be a better fit.

The short version

Use Firebase Trigger Email if you want:

  • a Firebase extension
  • a Firestore-based email queue
  • control over your own email content
  • to bring your own SMTP provider
  • to manage your own templates

Use EmailsDone if you want:

  • a small API or SDK call from trusted backend code
  • built-in transactional email templates
  • no custom HTML for common app emails
  • SES-backed sending without SMTP setup
  • sender setup through domain/DNS verification
  • less email infrastructure to own yourself

The difference is not just syntax.

It is the difference between triggering email delivery and having the common app-email layer already done.

What Firebase Trigger Email does

Firebase Trigger Email watches a Firestore collection.

When your app creates a document in that collection, the extension reads the document and sends an email based on the fields in it.

The flow looks roughly like this:

Your Firebase app
↓
Firestore email document
↓
Firebase Trigger Email extension
↓
Configured SMTP provider
↓
Recipient inbox

That is a neat Firebase-style pattern.

It means your app can trigger email by writing data to Firestore rather than calling an email API directly.

For example, your app might create a document containing:

{
  to: ["user@example.com"],
  message: {
    subject: "Welcome",
    html: "<p>Thanks for signing up...</p>"
  }
}

The extension then handles the send attempt through the configured mail server.

That can work well, especially for simple use cases.

The important difference: SMTP bridge vs email product

The biggest misunderstanding is thinking Firebase Trigger Email is Firebase’s equivalent of a full transactional email product.

It is not quite that.

The extension gives you a Firebase-native trigger mechanism, but you still need an actual email sending service behind it.

In practice, that usually means configuring an SMTP provider.

So the stack becomes:

Firebase
+
Firestore mail collection
+
Trigger Email extension
+
SMTP provider
+
your email templates

EmailsDone uses a different model:

Firebase app
↓
Firebase Function or trusted backend code
↓
EmailsDone template API
↓
Recipient inbox

With EmailsDone, your Firebase app still sends from trusted backend code. The API key still stays server-side. But you do not have to configure SMTP or create the common transactional email templates yourself.

That is why EmailsDone is not just an alternative trigger mechanism.

It is an alternative to building and operating the email layer yourself.

Where Firebase Trigger Email works well

Firebase Trigger Email can be a good choice when you want email to fit naturally into a Firestore-based architecture.

It works especially well if:

  • you already model backend work as Firestore documents
  • you want a mail collection that acts like a simple email queue
  • you are comfortable configuring SMTP
  • you want full control over the message content
  • you are happy creating and maintaining your own templates
  • you prefer extension-driven behaviour over direct API calls

For some Firebase apps, that is exactly the right shape.

You create a document, the extension picks it up, and the email is sent.

Simple enough.

Where the work still remains

The extension does not remove the whole email problem.

You still need to think about:

  • which SMTP provider to use
  • how that provider handles deliverability
  • how sender identities are configured
  • how templates are created and maintained
  • where HTML and text content live
  • how each app email should look
  • how template variables are passed
  • how failures are monitored
  • how retries and duplicate sends are handled
  • how unsubscribe or suppression behaviour should work for relevant emails

For one or two emails, that may be fine.

For a production app, the list tends to grow quickly:

  • welcome email
  • verify email
  • password reset
  • magic link
  • login code
  • invitation
  • notification
  • payment failed
  • trial ending
  • invoice
  • export ready

At that point, the hard part is not simply sending one message.

The hard part is owning the email system around your app.

What EmailsDone changes

EmailsDone takes a narrower approach.

Instead of asking you to build arbitrary emails from scratch, it gives you common transactional app emails as built-in templates.

Your Firebase app calls EmailsDone from trusted backend code, usually a Firebase Cloud Function.

For example:

await emailsDone
  .authentication()
  .welcome("https://app.example.com/get-started")
  .send("user@example.com");

Or:

await emailsDone
  .authentication()
  .passwordReset(resetUrl)
  .send("user@example.com");

Or:

await emailsDone
  .authentication()
  .verifyEmail(verificationUrl)
  .send("user@example.com");

The function is still yours.

The API key still stays server-side.

But the email template is already done.

Firebase Trigger Email vs EmailsDone

Question Firebase Trigger Email EmailsDone
What is it? A Firebase extension that sends email from Firestore documents A template-first transactional email API
Main trigger Firestore document creation SDK/API call from trusted backend code
Sending mechanism SMTP provider you configure SES-backed sending layer
Requires SMTP setup? Yes No
Requires backend code? Often yes, if you want safe app-controlled flows Yes, usually Firebase Functions or another trusted backend
Templates included? You manage the content/templates Common app email templates included
Custom HTML required? Usually, unless using simple text/template setup Not for built-in templates
Good for arbitrary custom emails? Yes Less flexible by design
Good for common app emails? Possible, but you own more setup Yes
Best fit Firebase-native email queue / trigger flow Production app emails with less setup

The useful way to think about it is:

Firebase Trigger Email = Firebase-native email plumbing
EmailsDone = template-first app email layer

Sender setup and domain ownership

One of the awkward parts of email is deciding how messages should be sent from your domain.

With SMTP-based setups, you need to configure whatever provider or mail server is doing the actual sending. Some developers try to use Gmail SMTP or a shared mail server, but that can introduce limits, account requirements and deliverability questions.

You should not need to buy inbox hosting just to send transactional app emails from your own domain.

EmailsDone is built around the transactional email model: verify the sending domain, add the required DNS records, then send app emails from that domain without running your own SMTP server.

That is a different type of ownership.

You still own your domain and your app flows.

You do not have to own the SMTP plumbing.

Developer ergonomics

The Firebase Trigger Email pattern feels natural if you already like Firestore-driven workflows.

Your app writes a mail document, and the extension does the rest.

The downside is that email becomes data you have to shape correctly:

await addDoc(collection(db, "mail"), {
  to: ["user@example.com"],
  message: {
    subject: "Welcome to Acme",
    html: "<h1>Welcome</h1><p>Thanks for signing up...</p>"
  }
});

That is not necessarily bad.

It is flexible.

But it means the app still owns the subject, HTML, structure and content.

With EmailsDone, the app expresses the email intent instead:

await emailsDone
  .authentication()
  .welcome(actionUrl)
  .send("user@example.com");

That is less flexible, deliberately.

The trade-off is that common app emails become much quicker to wire in.

When Firebase Trigger Email is the better fit

Use Firebase Trigger Email if you want a Firebase-native extension and you are happy managing the email content yourself.

It is a sensible choice when:

  • you want Firestore to act as your email queue
  • you already have your own SMTP provider
  • you want to design and control every email
  • your email requirements are unusual
  • you prefer extension configuration over adopting another API
  • you are comfortable owning the template layer

If you are building a custom email system, the flexibility can be useful.

When EmailsDone is the better fit

Use EmailsDone if your goal is not to build an email system at all.

It is a better fit when you want to add common transactional emails such as:

  • welcome
  • verify email
  • password reset
  • login code
  • magic link
  • notification
  • trial ending
  • payment failed
  • export ready

without designing and maintaining every template yourself.

EmailsDone is deliberately constrained.

That is the point.

Most apps do not need a unique password reset email layout. They need a clear, safe, production-ready password reset email that works.

The Firebase setup is still familiar

EmailsDone does not mean putting email keys in your frontend.

A safe Firebase setup still looks like this:

Frontend app
↓
Firebase Function
↓
EmailsDone
↓
Recipient inbox

The Firebase Function handles the trusted backend concerns:

  • authentication
  • App Check
  • validation
  • secrets
  • deciding whether the email should be sent

EmailsDone handles the email template and sending layer.

For example, from a Firebase Function:

import { onCall, HttpsError } from "firebase-functions/v2/https";
import { defineSecret } from "firebase-functions/params";
import { EmailsDone } from "emailsdone";

const emailsDoneApiKey = defineSecret("EMAILSDONE_API_KEY");

export const sendWelcomeEmail = onCall(
  {
    secrets: [emailsDoneApiKey],
    enforceAppCheck: true,
  },
  async (request) => {
    if (!request.auth?.token.email) {
      throw new HttpsError("unauthenticated", "You must be signed in.");
    }

    const emailsDone = EmailsDone.fromApiKey(emailsDoneApiKey.value());

    await emailsDone
      .authentication()
      .welcome("https://app.example.com/get-started")
      .send(request.auth.token.email);

    return { queued: true };
  }
);

The backend pattern is normal Firebase.

The email layer is the part that gets smaller.

Final recommendation

Firebase Trigger Email is useful if you want Firebase-native email plumbing and you are happy bringing your own SMTP provider, templates and email content.

EmailsDone is useful if you want to keep the Firebase backend pattern but avoid turning email into another project.

The difference is not:

Firestore document vs API call.

The real difference is:

Do you want to own the email layer, or do you just want the common app emails already done?

If you want full control, use the extension and build the templates.

If you want production app email with less faff, use a template-first API from your Firebase backend.

The function is still yours.

The domain is still yours.

The email template is already done.