Feature spec
IntermediateCategory
personalization-ux
Build with AI
3–6 hours with Lovable
Custom build
3–5 days custom dev
Running cost
$0–20/mo up to 1K users
Works on
Everything it takes to ship Email Integration — parts, prompts, and real costs.
Email integration needs an API provider (Resend is the best starting point — 3,000 free emails/month), an Edge Function that fires on database events or user actions, HTML email templates built with react-email, and an email_preferences table so users control which emails they receive. Lovable wires this in 4–6 hours. Running cost is $0–20/month for most early-stage apps — cost only scales with email volume.
What Email Integration Actually Involves
Email integration is not just sending a message — it is a small system with four moving parts: a trigger (user signs up, order ships, a daily digest fires), an HTML template that renders your brand correctly in Gmail, Outlook, and Apple Mail, a delivery API that handles the SMTP plumbing and tracks bounces, and a preferences layer so users can unsubscribe from optional emails without losing critical transactional ones. The pieces that trip up first builds are DNS authentication (DKIM and SPF records), the difference between transactional and marketing email rules, and duplicate sends when Supabase Auth's built-in email fires alongside your custom Edge Function. Get those three right and email is a solved problem.
What users consider table stakes in 2026
- Transactional emails (welcome, password reset, receipts) delivered within 5 seconds of the triggering event
- HTML email templates visually matching the app's branding — logo, brand color, font stack
- Unsubscribe link present in every marketing and digest email, with one-click opt-out
- Bounce and delivery status visible in the admin dashboard — hard bounces suppressed automatically
- Users can control which notification email types they receive via a preferences page in the app
- Emails render correctly in Gmail, Outlook 365, and Apple Mail without broken layouts
Anatomy of the Feature
Seven components — two data tables, three backend functions, one UI layer, and one DNS service. AI tools build five of these reliably; DNS setup and delivery webhooks need careful attention.
Email Trigger
BackendA Supabase Edge Function (Deno runtime) that fires on database events via Supabase Webhooks or pg_net, or on direct user actions (button click → Server Action → Edge Function). Imports the Resend SDK (@resend/node) or calls the SendGrid REST API with the recipient, template name, and variable payload.
Note: Name your Edge Functions by email type: send-welcome-email, send-order-confirmation, send-weekly-digest. One function per email type makes debugging and selective disabling much easier.
HTML Email Template
UIReact Email components (react-email, @react-email/components) for reusable, testable email layouts. Each template is a React component that accepts variables (userName, orderTotal, ctaUrl) and is rendered server-side to an HTML string via renderAsync(). react-email's preview server lets you see template changes in a browser before sending.
Note: Email clients interpret CSS inconsistently — react-email handles this by generating inline styles and table-based layouts that survive Outlook's rendering engine. Do not use CSS Grid or Flexbox in email templates.
Email Preferences Table
DataSupabase table email_preferences with boolean columns per email category: marketing, product_updates, transactional. The transactional flag should never disable truly critical emails (password reset, security alerts) — enforce this in the Edge Function logic, not the UI. Users manage their own row via a preferences page in the app.
Note: Add a preferences link to every email footer that takes the user directly to their preferences page — this reduces unsubscribe-all actions because users can opt out of marketing while keeping transactional.
Unsubscribe Handler
BackendA Supabase Edge Function or Next.js API route that receives unsubscribe link clicks. The link contains a signed JWT (HMAC-SHA256) encoding the user_id and email_type — no login required to unsubscribe. The handler validates the JWT, flips the relevant boolean in email_preferences, and returns a confirmation page.
Note: CAN-SPAM and GDPR both require one-click unsubscribe to work without requiring login. The signed JWT prevents unauthorized unsubscribes while keeping the flow frictionless.
Delivery Webhook Receiver
BackendAn Edge Function endpoint registered with Resend or SendGrid to receive real-time delivery events: delivered, bounced, complained, opened. Inserts events into email_delivery_log. On hard bounce, sets a suppressed flag on the user record to prevent future sends. Alerts admin for unusual bounce spike.
Note: Register this endpoint in the Resend dashboard under Webhooks immediately after setup — without it, you have no visibility into delivery failures and bounced users will keep receiving retried sends.
Admin Email Log
DataSupabase table email_delivery_log recording every email send attempt with its outcome: provider_message_id, status (sent, delivered, bounced, complained), delivered_at, opened_at. Drives an admin deliverability dashboard showing per-template delivery rates and flagging users with bounced addresses.
Note: Index on (user_id, sent_at DESC) for the per-user email history view and on (status, sent_at DESC) for the admin deliverability overview.
DKIM/SPF DNS Records
ServiceTXT DNS records added to your sending domain in your domain registrar. DKIM (DomainKeys Identified Mail) signs each email with a cryptographic key that receiving mail servers verify against your DNS. SPF (Sender Policy Framework) lists the servers authorized to send mail from your domain. Both are required for reliable Gmail inbox delivery — without them, emails land in spam.
Note: Propagation takes up to 48 hours. Verify both records using MXToolbox before sending to real users. Resend dashboard → Domains shows setup status with green/red indicators.
The data model
Two tables: email_preferences for user opt-in/out control, and email_delivery_log for admin deliverability visibility. Run this in the Supabase SQL editor.
1create table public.email_preferences (2 user_id uuid references auth.users on delete cascade primary key,3 marketing bool not null default true,4 product_updates bool not null default true,5 transactional bool not null default true,6 updated_at timestamptz not null default now()7);89alter table public.email_preferences enable row level security;1011create policy "Users can view own email preferences"12 on public.email_preferences for select13 using (auth.uid() = user_id);1415create policy "Users can upsert own email preferences"16 on public.email_preferences for insert17 with check (auth.uid() = user_id);1819create policy "Users can update own email preferences"20 on public.email_preferences for update21 using (auth.uid() = user_id);2223create table public.email_delivery_log (24 id uuid primary key default gen_random_uuid(),25 user_id uuid references auth.users on delete set null,26 template_name text not null,27 provider_message_id text,28 status text not null default 'sent',29 error_message text,30 sent_at timestamptz not null default now(),31 delivered_at timestamptz,32 opened_at timestamptz33);3435alter table public.email_delivery_log enable row level security;3637create policy "Service role can insert delivery log"38 on public.email_delivery_log for insert39 to service_role40 with check (true);4142create policy "Service role can update delivery log"43 on public.email_delivery_log for update44 to service_role45 using (true);4647create index email_log_user_idx48 on public.email_delivery_log (user_id, sent_at desc);4950create index email_log_status_idx51 on public.email_delivery_log (status, sent_at desc);Heads up: The service_role INSERT/UPDATE policy on email_delivery_log lets your Edge Functions (which use the service role key) write delivery events without exposing the log to regular users. Admins query it directly from the Supabase dashboard.
Build it — pick your path
Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.
Full custom build is the right choice when email is a core product feature — newsletter platforms, email clients, transactional systems requiring A/B testing, dedicated IPs, or multi-provider failover.
Step by step
- 1Build a template management system where HTML email templates are stored as database rows and editable without code deploys
- 2Implement SendGrid Dynamic Templates for A/B testing subject lines and CTA copy with statistical significance tracking
- 3Set up dedicated IP pools in SendGrid with a warm-up schedule for high-volume sending to protect sender reputation
- 4Build a suppression list synced across providers so a hard bounce in Resend prevents SendGrid fallover from resending
Where this path bites
- Dedicated IP warm-up (sending volume that ramps over 4–6 weeks) adds significant ops overhead before production sending begins
- Full deliverability setup (DKIM, SPF, DMARC, BIMI) plus reputation monitoring is a week of standalone work separate from feature development
Third-party services you'll need
Email sending requires a third-party API — you cannot send production email reliably without one. DNS authentication is a one-time free setup in your domain registrar.
| Service | What it does | Free tier | Paid from |
|---|---|---|---|
| Resend | Primary transactional email API — simplest integration, excellent developer experience, built-in react-email support | 3,000 emails/month, 100 emails/day | Pro $20/mo (50K emails); Business $90/mo (100K emails) (approx) |
| SendGrid | Alternative to Resend — larger ecosystem, Dynamic Templates for A/B testing, dedicated IP options for high volume | 100 emails/day (no monthly cap on free tier) | Essentials $19.95/mo (50K emails); Pro $89.95/mo (150K emails) (approx) |
| react-email | Open-source React component library for building cross-client compatible HTML email templates | Fully free (MIT license) | Free — no paid tier |
| Supabase pg_net | Built-in HTTP extension for calling email APIs directly from PostgreSQL database triggers and functions | Included in all Supabase plans | No additional cost — included in Supabase Pro ($25/mo) (approx) |
Swipe the table sideways to see pricing.
What it costs to run
Drag through the tiers to see how your monthly bill scales with users — no surprises later.
Estimated monthly running cost
Resend free tier (3,000 emails/month) easily covers welcome + transactional for 100 users. Supabase free tier handles email_preferences and delivery log storage.
Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.
What breaks when AI tools build this
The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.
Emails go straight to Gmail spam
Symptom: DKIM and SPF DNS records are not configured for the sending domain. Emails arrive from Resend's shared IP pool without domain authentication — Gmail's spam filter flags them as potentially spoofed. This is the single most common email integration failure and it is invisible in development because you see 'sent' status in the API but recipients never see the message.
Fix: Go to the Resend dashboard → Domains → Add Domain, then copy the two DKIM TXT records and the SPF TXT record shown there into your domain registrar's DNS settings. Wait up to 48 hours for propagation, then verify at MXToolbox (search your domain with the TXT check). All three records must show green before sending to real users.
Users receive two welcome emails after signup
Symptom: Supabase Auth has built-in email confirmation enabled by default — it sends a welcome/confirmation email immediately on signup. If your custom Edge Function also fires on the auth.users insert event, the user gets both emails within seconds. This burns sender reputation and confuses users.
Fix: Decide which system owns email: either use Supabase Auth's built-in templates (configure them in Supabase dashboard → Authentication → Email Templates) or disable them and own all emails yourself via Edge Functions. To disable: Authentication → Email Templates → toggle off 'Confirm signup'. Do not run both.
Unsubscribe link returns 404 after deployment
Symptom: The unsubscribe Edge Function was generated but its URL was not set as an environment variable in the deployed app, or the function was not deployed to production (it exists locally but not on Supabase). The signed JWT in the unsubscribe URL contains an expiry, so old links also fail after the default 24-hour window.
Fix: Verify the Edge Function is deployed: open Supabase dashboard → Edge Functions and confirm the function shows 'Active' status. Add UNSUBSCRIBE_SECRET to Supabase Secrets (Cloud tab in Lovable, or Supabase dashboard → Edge Functions → Secrets). Set JWT expiry to 30 days in the signing code so links in weekly digests remain valid.
Edge Function times out sending emails with large templates
Symptom: react-email's renderAsync() compiles large templates with many conditional sections on every invocation. In cold-start Edge Functions (which have no warm state), this compilation takes 8–15 seconds, exceeding the default 10-second Edge Function timeout and causing a 504 error.
Fix: Pre-render templates at build time into static HTML strings stored in Supabase Storage as .html files. The Edge Function fetches the pre-rendered HTML and performs string replacement for variables ({{userName}}, {{ctaUrl}}) rather than re-rendering the React component. This reduces per-send execution time from 10+ seconds to under 1 second.
Best practices
Use Resend for new projects — the developer experience, react-email native support, and 3,000 free emails/month make it the default choice for apps under 50K monthly sends
Never store RESEND_API_KEY or SendGrid keys in frontend code — always use Supabase Secrets (Edge Functions) or Vercel env vars (Next.js API routes), server-side only
Set up DKIM and SPF DNS records before sending to any real user — spam folder delivery poisons your sender reputation in the first 24 hours
Keep the transactional boolean in email_preferences always true for critical flows (password reset, security alerts) — only marketing and product_updates should be user-toggleable
Add the unsubscribe URL with a signed JWT to every marketing and digest email footer — CAN-SPAM and GDPR both require one-click unsubscribe with no login barrier
Cache the email_preferences lookup in your Edge Function using the session — don't query the table for every send in a batch digest run
Log every send attempt to email_delivery_log before the API call, then update status on the webhook callback — this gives you a complete audit trail even if the API call times out
Pre-render react-email templates to static HTML at build time and store in Supabase Storage — cold-start template compilation causes Edge Function timeouts at scale
When You Need Custom Development
Lovable and V0 handle standard transactional email integration reliably. These scenarios require deeper engineering:
- The app is itself a newsletter or email client product requiring full inbox management, IMAP/SMTP integration, or email threading
- Regulatory requirement to maintain CAN-SPAM and GDPR-compliant suppression lists with full audit trail and data export capability
- High-volume sending (100,000+ emails per day) requiring dedicated IP pools, a warm-up schedule over 4–6 weeks, and reputation monitoring
- Transactional email templates need A/B testing, send-time optimization, and engagement analytics beyond basic open/click tracking
RapidDev builds these features for production
Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.
Frequently asked questions
Which email provider is best for a new app — Resend or SendGrid?
Resend for most new projects. It has native react-email support, a cleaner API, and 3,000 free emails per month with no daily cap limits as restrictive as SendGrid's 100/day free tier. SendGrid makes more sense if you need Dynamic Templates for A/B testing subject lines, dedicated IP management for high-volume sending, or if your team already has a SendGrid account with sender reputation built up.
Do I need to verify my domain to send emails?
Yes, for production. You can send from an unverified domain during development, but emails will land in spam for most recipients. Domain verification means adding two DKIM TXT records and one SPF TXT record to your domain's DNS — Resend's dashboard shows you exactly what to copy. Allow up to 48 hours for DNS propagation before testing deliverability.
What is the difference between transactional and marketing emails?
Transactional emails are sent in direct response to a user action — welcome email after signup, password reset, order confirmation, security alert. Marketing emails are sent proactively — weekly digests, promotional campaigns, product announcements. CAN-SPAM and GDPR apply stricter rules to marketing emails (mandatory unsubscribe, no deceptive headers) while transactional emails have more flexibility. Never put a user into a 'no email' state that blocks their password reset.
How do I stop emails going to spam?
Three steps cover 95% of spam filter failures: (1) Set up DKIM and SPF records for your sending domain — do this before your first production send. (2) Start with low volume and gradually increase to build sender reputation. (3) Maintain a clean list by suppressing hard bounces immediately and processing unsubscribes within 10 days as required by CAN-SPAM. Gmail's Postmaster Tools dashboard shows your domain reputation once volume picks up.
Can users unsubscribe from specific email types?
Yes — the email_preferences table stores boolean flags per category (marketing, product_updates, transactional). Each category maps to a toggle in your app's notification settings page. The unsubscribe link in email footers should specify which category to unsubscribe from, not silently disable all email. Transactional should always remain true unless the user explicitly requests account deletion.
How do I preview my email template before sending?
react-email includes a local preview server — run it in a separate browser tab outside your main app to see templates rendered with test data. The Resend dashboard also shows a preview of recent sends in the Logs tab. For cross-client testing (Gmail vs Outlook vs Apple Mail rendering), Litmus or Email on Acid provide screenshot previews across 90+ clients — useful before a major launch but not required for every iteration.
What happens when an email bounces?
Hard bounces (permanent delivery failure — the address doesn't exist) should be recorded in email_delivery_log with status 'bounced' and suppressed from all future non-critical sends. Soft bounces (temporary failure — mailbox full) can be retried 2–3 times over 24 hours. Resend and SendGrid both send bounce events to your registered webhook endpoint automatically — your delivery webhook Edge Function updates the log.
Is there a free tier that covers small apps?
Resend's free tier (3,000 emails/month, 100/day) covers welcome and transactional emails for apps under roughly 300 active users sending 10 emails each per month. The 100/day cap matters if you trigger batch sends — a welcome email campaign for 200 new users in a day would hit the cap. Upgrade to Resend Pro ($20/mo, approx) when you add weekly digests or the user base grows past 300.
Need this feature production-ready?
RapidDev builds email integration into real apps — auth, database, payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.