Newsletters are all the rage since 2020! A medium that everyone thought was pretty much extinct is back, stronger than ever!
This fact partly informed my decision to build a site from scratch.
It will serve as the 'top of the funnel' for my newsletter sign-ups.
On to the technical details...
Integrating with an email marketing tool
I chose ConvertKit because I can start free and build it up in time. It's built with the small creator in mind by an Indie Hacker.
I wanted to start building my email list. However, since I don't yet have a lot of content, it didn't make sense to take a too aggressive stance and push newsletter sign-ups on my users.
I decided to add a newsletter form in my site's footer.
GatsbyJS doesn't have a straightforward way to integrate an email marketing tool (or ConvertKit explicitly).
I had two options:
- write a simple HTML form myself and post directly to ConvertKit
- write a React component to display ConvertKit's Javascript-based form
I settled on the latter since it had nicer UX and a few extra features built-in, which I'll be discussing below.
Since Gatsby pre-renders all pages, you can't directly include a script tag - it would be interpreted at build time and would not work for the end-user.
The solution was to use the DOM to create a script element and display it.
Here is the component's code:
import * as React from "react";
import { Component } from "react";
class Newsletter extends Component {
componentDidMount() {
const script = document.createElement("script");
script.src = "CONVERTKIT-FORM-URL";
script.async = true;
script.setAttribute("data-uid", "CK-UID");
this.instance.appendChild(script);
}
render() {
return (
<div>
<h3>Subscribe to my newsletter</h3>
<div ref={(el) => (this.instance = el)}></div>
</div>
);
}
}
export default Newsletter;
ConvertKit
Once I finished the coding part, I had to customize ConvertKit.
I didn't want to use my personal Gmail as the sender of the newsletter, but I also did not want to set up a separate account or pay for email hosting.
Email forwarding
I created a simple email forwarding rule in https://forwardemail.net to forward all emails to my Gmail account.
The first step in that process is to verify you own the domain. After creating an account, I updated my domain's DNS records.
I manage all my domains through https://cloudflare.com, and I automate any management operations using https://www.terraform.io/.
I won't go into Terraform low-levels (their documentation is excellent), but here is the configurations I use:
variable "zone_id_com_mihaibojin" {
default = "GET-THIS-FROM-CLOUDFLARE"
}
resource "cloudflare_record" "com_mihaibojin_mx1" {
zone_id = var.zone_id_com_mihaibojin
name = "@"
value = "mx1.forwardemail.net"
priority = 10
type = "MX"
}
resource "cloudflare_record" "com_mihaibojin_mx2" {
zone_id = var.zone_id_com_mihaibojin
name = "@"
value = "mx2.forwardemail.net"
priority = 20
type = "MX"
}
resource "cloudflare_record" "com_mihaibojin_forwardemail_spf" {
zone_id = var.zone_id_com_mihaibojin
name = "@"
value = "v=spf1 a mx include:spf.forwardemail.net -all"
type = "TXT"
}
resource "cloudflare_record" "com_mihaibojin_forwardemail_ver" {
zone_id = var.zone_id_com_mihaibojin
name = "@"
value = "forward-email-site-verification=GET-THIS-FROM-FORWARDEMAIL"
type = "TXT"
}
Updated your domain's DNS is then a simple command away!
terraform plan && terraform apply
Configuring ConvertKit
After I configured email forwarding and verified I could receive emails, it was time to set up ConvertKit. After adding my email, I waited for the confirmation email and clicked the provided link.
SPF and DKIM
I also set up "Sender Policy Framework" (SPF) and "Domain Keys Identified Mail" (DKIM). Read more here about why it's essential to verify your sending emails (spoiler alert: primarily so that your emails don't end up as Spam).
Once again, I used Terraform to automate this step:
resource "cloudflare_record" "om_mihaibojin_convertkit_cname1" {
zone_id = var.zone_id_com_mihaibojin
name = "ckespa.mihaibojin.com"
value = "spf.dm-2m2gkx6y.sg7.convertkit.com"
type = "CNAME"
}
resource "cloudflare_record" "com_mihaibojin_convertkit_cname2" {
zone_id = var.zone_id_com_mihaibojin
name = "cka._domainkey.mihaibojin.com"
value = "dkim.dm-2m2gkx6y.sg7.convertkit.com"
type = "CNAME"
}
DMARC
I went the extra mile and also configured DMARC. By all accounts, you shouldn't bother when you're not sending a large volume of emails, but I wanted to start right and be prepared for future success!
One of DMARC's policy settings is p=none
, which means data will be collected but not actioned - this is what you want in this scenario.
To monitor collected data, I created an account on https://dmarcian.com/. They provide a service that analyzes your data and reports on any threats.
Here is how the DNS record configuration for DMARC looks like in Terraform:
resource "cloudflare_record" "com_mihaibojin_dmarc" {
zone_id = var.zone_id_com_mihaibojin
name = "_dmarc.mihaibojin.com"
value = "v=DMARC1; p=none; rua=mailto:GET-THIS-FROM-DMARCIAN;"
type = "TXT"
}
You can follow these resources for more information:
GDPR and newsletter sign-ups
I wrote about privacy and GDPR in the European Union in my privacy-friendly analytics article.
ConvertKit has an option to allow users to explicitly opt-in for features that fall under GDPR's guidelines. However, the outcome is that users will be redirected to a separate page after sign-up.
By default, ConvertKit sends users to https://app.convertkit.com/confirm-subscription. Since that takes the user away from my site, I replicated the same message at https://mihaibojin.com/confirm-subscription so that users can navigate back to the homepage or some other article!
The URL can be configured for each form created in ConvertKit, by going to Settings -> Incentive
and adding the URL.
Conclusion
I hope you found this post useful. If you have any thoughts or comments, please share your thoughts on this thread.
That's all for now. In the future, I may drop the ConvertKit Javascript code and implement an HTML form since I'm not particularly fond of tracking JS running on my site.
Until then, stay safe!