Building a newsletter form in GatsbyJS with ConvertKit
Tags: newsletter, convertkit, gatsby, spf
Published: 2021-06-19 19:26:02

---
title: Building a newsletter form in GatsbyJS with ConvertKit
published: true
date: 2021-06-19 00:00:00 UTC
tags: Newsletter,ConvertKit,GatsbyJS,SPF
series: Building a developer blog with GatsbyJS
canonical_url: https://MihaiBojin.com/personal-site/newsletter-convertkit
---

![Photo: Typing machine](https://MihaiBojin.com/static/51e50d3ef5259bb5b36f419a184b1ce0/092b2/florian-klauer-mk7D-4UCfmg-unsplash.jpg)<figcaption>"Photo by <a href="https://unsplash.com/@florianklauer?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Florian Klauer</a> on <a href="https://unsplash.com/s/photos/newsletter?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a>"</figcaption>

🔔 This article was originally posted on my site, [MihaiBojin.com](https://MihaiBojin.com/personal-site/newsletter-convertkit?utm_source=DevTo&utm_medium=organic&utm_campaign=rss "MihaiBojin.com"). 🔔

* * *

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](https://mihaibojin.com/personal-site/building-a-personal-site?utm_source=DevTo&utm_medium=organic&utm_campaign=rss).

It will serve as the 'top of the funnel' for my newsletter sign-ups.

On to the technical details...

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#integrating-with-an-email-marketing-tool)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](https://www.indiehackers.com/interview/convertkit-2fcb58f1fa).

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](https://reactjs.org/) 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;
```

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#convertkit)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.

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#email-forwarding)Email forwarding

I created a simple email forwarding rule in [https://forwardemail.net](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](https://cloudflare.com), and I automate any management operations using [https://www.terraform.io/](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
```

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#configuring-convertkit)Configuring ConvertKit

After I configured email forwarding and verified I could receive emails, it was time to set up ConvertKit. After [adding my email](https://app.convertkit.com/account/edit#email_settings), I waited for the confirmation email and clicked the provided link.

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#spf-and-dkim)SPF and DKIM

I also set up "Sender Policy Framework" (SPF) and "Domain Keys Identified Mail" (DKIM). [Read more here](https://help.convertkit.com/en/articles/2502558-using-a-verified-domain-for-email-sending) 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"
}
```

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#dmarc)DMARC

I went the extra mile and also configured [DMARC](https://help.convertkit.com/en/articles/4534360-dmarc-what-is-it-and-should-you-use-it). 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/](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:

- [https://www.newslettercrew.com/blog/what-is-dmarc](https://www.newslettercrew.com/blog/what-is-dmarc)
- [https://dmarcian.com/start-dmarc/](https://dmarcian.com/start-dmarc/)

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#gdpr-and-newsletter-sign-ups)GDPR and newsletter sign-ups

I wrote about privacy and GDPR in the European Union in my [privacy-friendly analytics](https://mihaibojin.com/personal-site/privacy-friendly-analytics-in-gatsbyjs?utm_source=DevTo&utm_medium=organic&utm_campaign=rss) 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](https://app.convertkit.com/confirm-subscription). Since that takles the user away from my site, I replicated the same message at [https://mihaibojin.com/confirm-subscription](https://mihaibojin.com/confirm-subscription?utm_source=DevTo&utm_medium=organic&utm_campaign=rss) 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.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#conclusion)Conclusion

I hope you found this post useful. If you have any thoughts or comments, please share your thoughts [on this thread](https://twitter.com/mihaibojin/status/1406043106915594241).

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!

* * *

If you liked this article and want to read more like it, [please subscribe to my newsletter](https://motivated-founder-807.ck.page/db1cf284bf "newsletter link"); I send one out every few weeks!