Newsletter
The newsletter forms collect email addresses into a table you own, or straight into your Mailchimp audience, and each new subscriber gets a welcome email. With Supabase you then write to your list with the tool of your choice (export the table, or connect it to a mailing service); with Mailchimp the list is ready to send to.
Templates: all except Aurora (Aurora has a waitlist instead).
Services: Supabase or Mailchimp stores
the sign-ups; Resend sends the welcome email (see
Contact form for setting up Resend). Every template takes either
(a site made earlier gets it with npx wptom update).
| Where sign-ups go | Settings | Guide |
|---|---|---|
| A Supabase table | SUPABASE_URL, SUPABASE_SECRET_KEY, SUBSCRIBERS_TABLE |
Steps 1 to 6 below |
| A Mailchimp audience | MAILCHIMP_API_KEY, MAILCHIMP_AUDIENCE_ID, MAILCHIMP_DOUBLE_OPT_IN |
Mailchimp instead of Supabase |
Set up one of them. If both are set, Mailchimp is used and Supabase is ignored.
Where the forms are
Section titled “Where the forms are”Usually on the journal or news page (the “Tips by email” form in Handy, “Reminders” in Mint), and in the home page section of the same name when the template has one. A few templates use the same list for other sign-ups too:
| Template | Other forms that add to the newsletter list | source value |
|---|---|---|
| Wag, Whisker | Volunteering sign-up on the Help page | volunteers |
| Stride | Volunteers on the Race day page | volunteers |
| Chalk | The form on the On tap page | taps |
Every sign-up is stored with a source (where the form was), so you can tell the lists apart.
What happens on sign-up
Section titled “What happens on sign-up”- The visitor enters an email and clicks the button.
- The address is checked (one
@, a domain with a dot, at most 254 characters), trimmed and lowercased. A hidden field catches bots. - The address is added to your table. If it is already there, nothing changes and the form shows the “already on the list” text.
- Only for a new address, the welcome email is sent. If sending fails, the sign-up still counts and the error is logged.
- The form shows the “joined” text. Without JavaScript, the visitor lands on the journal or news page instead.
Without keys
Section titled “Without keys”- No Supabase or Mailchimp settings: sign-ups are kept in the memory of the running server. Fine for
trying the form; they disappear when
npm run devstops. Never use this online. - No Resend key: the welcome email isn’t sent; the terminal prints
[email] to someone@example.com: Welcome subjectinstead.
1. Create a Supabase project
Section titled “1. Create a Supabase project”- Sign up at supabase.com and click New project.
- Pick a name, a database password (store it somewhere safe) and a region close to your visitors. Wait until the project is ready.
2. Create the table
Section titled “2. Create the table”Open SQL Editor in your project, paste this and click Run:
create table public.subscribers ( email text primary key, source text, created_at timestamptz not null default now());
-- Row level security on, with no policies: only your site's secret key can read or write.alter table public.subscribers enable row level security;These are exactly the columns the site writes: email, source and created_at. The
primary key on email is what makes a second sign-up with the same address a no-op, so keep
it.
Want another table name? Create it under that name and set SUBSCRIBERS_TABLE (the default is
subscribers).
3. Copy the project URL and a secret key
Section titled “3. Copy the project URL and a secret key”- In Supabase, open Project Settings → Data API (or the Connect button) and copy the
Project URL, e.g.
https://abcdefgh.supabase.co. - Open Project Settings → API Keys and create or copy a secret key. Projects that
still show the older keys can use the service role key instead. Never use the publishable or
anonkey: it can’t write to a table with row level security.
4. Add the settings to .env
Section titled “4. Add the settings to .env”SUPABASE_URL=https://abcdefgh.supabase.coSUPABASE_SECRET_KEY=your-secret-keySUBSCRIBERS_TABLE=subscribersFor the welcome email, also set RESEND_API_KEY and EMAIL_FROM (see
Contact form).
Restart npm run dev.
The secret key is used only on the server. It never reaches the browser.
5. Try it on your computer
Section titled “5. Try it on your computer”- Open the page with the newsletter form and sign up with an address you can check.
- The form shows its “joined” text.
- In Supabase, open Table Editor → subscribers: a new row with your email, the
sourceand the time. - Check your inbox for the welcome email (or, without a Resend key, the
[email]line in the terminal). - Sign up again with the same address: the form says you’re already on the list, no new row appears and no second email is sent.
6. Put it online
Section titled “6. Put it online”- In Vercel, Settings → Environment Variables: add
SUPABASE_URL,SUPABASE_SECRET_KEYandSUBSCRIBERS_TABLE(andRESEND_API_KEY,EMAIL_FROMif not there yet). - Redeploy and sign up once on the live site. Check the row in Supabase.
Mailchimp instead of Supabase
Section titled “Mailchimp instead of Supabase”Skip steps 1 to 6 and send sign-ups to a Mailchimp audience instead. Everything else works the same: the form, the duplicate check and the welcome email.
1. Copy your API key and audience id
Section titled “1. Copy your API key and audience id”- Sign in at mailchimp.com (or create a free account).
- Click your profile picture → Profile → Extras → API keys → Create A Key. Name it (e.g.
my-site) and copy it. You see it only once. It ends with a dash and your data center, e.g.…-us21: keep that suffix, the site uses it to find your account’s server. - Open Audience → All contacts → Settings → Audience name and defaults and copy the
Audience ID (letters and digits, e.g.
a1b2c3d4e5). It isn’t the audience’s name.
2. Add the settings to .env
Section titled “2. Add the settings to .env”MAILCHIMP_API_KEY=0123456789abcdef0123456789abcdef-us21MAILCHIMP_AUDIENCE_ID=a1b2c3d4e5MAILCHIMP_DOUBLE_OPT_IN=falseMAILCHIMP_DOUBLE_OPT_IN:false(default) adds the address as subscribed at once.truemakes Mailchimp send its own confirmation email first; the contact shows as Pending and becomes subscribed when they click the link. Your site’s welcome email still goes out right after the sign-up, so with double opt-in consider asking for the confirmation in its text.- Each contact gets the form’s
source(e.g.home,journal,volunteers) as a tag, so you can segment by where people signed up. - An address that is already in the audience (subscribed, pending or unsubscribed) is left as it is: the form shows the “already on the list” text and no welcome email is sent. The site never resubscribes someone who unsubscribed.
Restart npm run dev. The keys are used only on the server.
3. Try it on your computer
Section titled “3. Try it on your computer”- Sign up on your site with an address you can check. The form shows its “joined” text.
- In Mailchimp, Audience → All contacts: the address is there, with the source as a tag (status Subscribed, or Pending with double opt-in, plus Mailchimp’s confirmation email in your inbox).
- The welcome email arrives (or the
[email]line appears in the terminal). - Sign up again with the same address: “already on the list”, no second email.
4. Put it online
Section titled “4. Put it online”Add MAILCHIMP_API_KEY, MAILCHIMP_AUDIENCE_ID and MAILCHIMP_DOUBLE_OPT_IN in Vercel
(Settings → Environment Variables), redeploy and sign up once on the live site.
The welcome email
Section titled “The welcome email”Its texts are in the content editor: Interface texts → Emails, the newsletter (or tips,
reminders…) welcome email: subject, heading, text, button label and signature. {name} is
replaced with your site’s name. The button links to your journal or news page. How it looks
and how to test it: Emails.
Using the list
Section titled “Using the list”The table is yours. In Supabase’s Table Editor you can export it as CSV, or connect it to your mailing tool. To remove someone, delete their row. With Mailchimp, the audience is your list: write campaigns to it, or to a tag.
Troubleshooting
Section titled “Troubleshooting”| What you see | Why, and what to do |
|---|---|
| The “something went wrong” text | Supabase refused the row. The terminal (online: Vercel → Logs) shows [waitlist] store failed with Supabase’s answer. Common causes: the table doesn’t exist or has another name (set SUBSCRIBERS_TABLE), a column is missing, or the key isn’t a secret key |
An error about on_conflict or a missing unique constraint |
The email column isn’t the primary key (or unique). Recreate the table with the SQL above |
| Rows appear, but no welcome email | Look for [waitlist] onJoin failed in the log: usually EMAIL_FROM isn’t on a verified Resend domain. Without RESEND_API_KEY no email is sent at all |
| Sign-ups vanish | Neither Supabase nor Mailchimp is configured, so they were in memory. Check both SUPABASE_URL and SUPABASE_SECRET_KEY (or both Mailchimp settings) are set, then restart or redeploy |
| “Mailchimp API key must end with its data center” in the log | The key was copied without its -us21-style suffix. Copy the whole key again |
Mailchimp error 401 in the log |
The key is wrong or was deleted in Mailchimp. Create a new one |
Mailchimp error 404 in the log |
MAILCHIMP_AUDIENCE_ID isn’t an audience of this account: copy the Audience ID, not the name |
Mailchimp error 400 with another reason |
Mailchimp refused that address (its reason follows in the log, e.g. an address it considers fake, or one that was permanently deleted from the audience). Other addresses still work |
| Sign-ups go to Supabase, not Mailchimp | One of the two Mailchimp settings is missing. Both are needed |