This Blog's Newsletter Now Runs on Emit

The signup form at the bottom of this post now points at emit, the RSS-to-newsletter service I launched on Friday. When I publish here, emit picks up the RSS feed and sends the post to subscribers. This site is now customer zero, and this post is about what that wiring looks like and what it taught me.
The integration is one partial
Hugo themes make this kind of swap pleasant. The subscribe form is a single partial template, and Hugo lets a site override any theme file by shadowing its path. So the entire migration was replacing one subscribe.html in my project’s layouts/partials/ directory, no theme fork required.
The form itself posts to emit’s public subscribe endpoint with a publishable token in a hidden field:
<form action="https://api.rssemit.com/v1/public/subscribe" method="post">
<input type="hidden" name="token" value="pk_...">
<input type="email" name="email" required>
<button type="submit">Subscribe</button>
</form>
That pk_ token is committed to a public repo, and that’s fine by design. It can do exactly one thing: create a pending subscriber who then has to click a confirmation link. Every signup is double opt-in, so the worst an abuser can do with the token is send someone a single confirmation email they ignore. Secrets that don’t need to be secret are my favorite kind.
On top of the plain form there’s a little progressive enhancement: JavaScript intercepts the submit, fires the POST in the background, and swaps in a “check your inbox” message inline. If JavaScript is off, the form still works the old way and emit redirects you to a confirmation page. And there’s a honeypot field, an invisible input that only bots fill in, which quietly filters most drive-by form spam before it costs anyone a confirmation email.
Migrating the list
The previous provider had a small list, and moving it raised a question I hadn’t designed for: what do you do with subscribers who already confirmed somewhere else? Making them re-confirm would burn half the list to satisfy a rule they’d already satisfied. So the import goes through the API with their verified status intact, but only for addresses that were verified at the source. Anyone who signed up and never confirmed didn’t make the trip. A smaller, cleaner list beats a bigger suspicious one, especially when mailbox providers are grading your sending domain on every send.
What dogfooding surfaced
The embarrassing, useful stuff you only find by being your own customer:
DNS is the real onboarding. Creating an account takes a minute. Getting DKIM records into DNS and waiting for them to verify is where a new user actually lives, and the dashboard initially treated it as an afterthought. Sitting in that waiting room myself is why the domains page now distinguishes “waiting on the provider” from “waiting on your DNS records,” because those need different reactions from the user.
The confirmation email is your first impression. Mine originally looked like a subpoena. Plain, cold, no indication of what you’d subscribed to. It’s now branded and warm, because I received one and winced.
Styling the form matters more than I thought. The stock embed looked like a stock embed. Ten minutes of CSS to match this site’s typography roughly doubled how much I trusted my own product, which is irrational and exactly how every visitor evaluates a signup form.
hidden loses to any display rule you write. The “check your inbox” confirmation lives in an empty paragraph that carries the HTML hidden attribute until the script fills it in. It rendered anyway, as a mysterious empty bordered box under the form, because I had styled it with display: flex for the checkmark-plus-text layout. The hidden attribute is not magic: browsers implement it as display: none in the user-agent stylesheet, and any author-level display declaration beats the user-agent one. So the element was hidden in the accessibility tree and perfectly visible on the page. If you style something you plan to toggle with hidden, you owe it an explicit rule:
.subscribe__status { display: flex; }
.subscribe__status[hidden] { display: none; }
Worth internalizing beyond this one case, because the same trap catches <details>, [aria-hidden], and anything else where a semantic attribute implies a default style that your own CSS quietly outranks.
None of these were bugs a test suite would catch. They were all discovered by one person subscribing to his own blog and paying attention to how it felt.
If you want posts from here in your inbox, the form is right below. It’s a short hop from there to a confirmation email, and you’ll be riding the same pipes this post just described.
Stay in the loop
Get notified when I publish new posts. No spam, unsubscribe anytime.