Collect emails on your landing page without a mailing service

Collect emails on your landing page without a mailing service

Few years ago I was building small product for which I had a fancy landing page. It had a form to collect emails and all. The page was static and I wanted to integrate a Mailchimp form somewhere in between. I had no clues how to do it, so I went head-down learning. After 2 days of failing, I made it.

But it looked awful.

Fast forward 2021, I also created another landing page for a small side project I am building. But this time, taught by the past experience, I tried to find another solution. As I am building a Web app, I have already gained some experience in handling Firebase operations via its Javascript APIs. This, in my head looks like this: Text

But the solution is really simple, and something they don't tell you at school.

The use-case is the following:

      🔸 a user subscribes to your waiting list
      🔸 his email gets persisted (for future processing)
      🔸 you collect all subscribed emails and import them somewhere

To solve the problem we will use Firebase Realtime database (not Firestore).

The configuration

Create your Firebase project and add a Web App to it in the Console.

web.png

Fill-in the form and at the end of the wizard you will be provided with your config code. It looks something like this:

config.png

Now take that config and add it at the beginning of your <body>.
Also, the necessary scripts need to be loaded before that block:

<script src="https://www.gstatic.com/firebasejs/8.4.1/firebase-app.js" />
<script src="https://www.gstatic.com/firebasejs/8.4.1/firebase-database.js" />

Create the database

As next step you will have to create the Realtime database. From the Console go to Realtime Database in the sidebar and press Create Database.

The code

We will need a form from which we allow the user to type in the email. Something like this:

<div class="hero-container mx-1 mx-lg-5 mb-5">
  <form onsubmit="saveToFirebase(); return false;" class="cta-input w-75 mb-2">
    <input id="emailField" type="email" class="cta-input_input flex-grow-1" placeholder="Email">
    <button class="cta-input_btn">I want to know more</button>
  </form>
  <p id="success" class="p_success mb-3">Thanks for showing your interest in my app!</p>
  <p id="failure" class="p_failure mb-3">Something bad happened and your subscription didn't succeed.</p>
</div>

And small CSS to support the success and failure messages:

.p_success {
  color: green;
  font-size: .8rem;
  font-weight: 300;
  line-height: 1.5;
  opacity: 0.95;
  display: none;
}

.p_failure {
  color: maroon;
  font-size: .8rem;
  font-weight: 300;
  line-height: 1.5;
  opacity: 0.95;
  display: none;
}

The success and failure paragraphs have a property display: none; which is a Cloak of invisibility —makes them invisible by default. Then we show them when needed.

As you can see in the HTML the form is defined to call the saveToFirebase() JavaScript function onSubmit. In the very same line, after the saveToFirebase() there is also the line return false;. This makes the page stay unrefreshed after submitting the data. Without this, after successful submit the page will be reloaded and the calls to Firebase will never happen. Took me a day to figure this out.

In that form there is an input field with id emailField. This is the field where the email is put. When the button next to it is pressed, the form will execute the saveToFirebase() function and that's where the magic should happen. So we need this scripts. I put them just right before closing the </body>. There were already some scripts there so I just followed. No idea why.

This are my calls:

<script>
  function saveToFirebase() {

    const emailValue = document.getElementById('emailField').value;

    const database = firebase.database().ref();
    const usersRef = database.child('users');

    if(validateEmail(emailValue)) {
      usersRef.push({email: emailValue})
        .then(function(snapshot) {
          success();
        }, function(error) {
          failure();
        })
      }
  }

  function validateEmail(mail) {
    if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(mail)) {
      success();
      return true;
    } else {
      failure();
      return false;
    }
  }

  function failure() {
    // make the failure paragraph visible
    document.getElementById("failure").style.display = "block";
    // make the success paragraph invisible
    document.getElementById("success").style.display = "none";
  }

  function success() {
    // make the failure paragraph invisible
    document.getElementById("failure").style.display = "none";
    // make the success paragraph visible
    document.getElementById("success").style.display = "block";
  }

</script>

The validation step might not be necessary if you use something like Bootstrap or Tailwind, just as I do, but I have it just in case. Makes it also fancier.

So what will this do?

  1. Will validate the email
  2. Will call saveToFirebase function, that will
  3. Create an entry to your Firebase Realtime database with a random ID and email field with the value set by the user
  4. will show a success message under the email field if that call succeeds, or failure if it fails

So if you go to your database at this point, you will see:

The final result for the user will look like this:

subscribe.gif

Next steps

Next you can do two more things:

  1. Create a cloud function that after creating and entry to the users collection will send a confirmation email
  2. Export all emails and import them in a mailing service so that you can make use of them

Both of these cases will be covered in some future article of mine. Thanks for reading!