Markus Spiske 466enaluhly Unsplash.jpg

Ghost Registration Form

The Ghost platform allows the user to easily capture registration information using the built-in subscription mechanism. Subscription information is stored in the internal database and may be read via the admin UI, downloaded as a CSV or accessed via the API. Once a user enters their address and clicks subscribe, Ghost automatically validates the user’s address by sending them an email containing a confirmation link.

The basic subscription form looks sometime like this:
Ghost subscription form(You an see a working example at the bottom of this page)

The mechanism powering this form can be utilized for a variety of purposes, all feeding into the same master list of registered users.

In a previous article, we created an exit-intent popup using Bootstrap 4.  We’re now going to use the Ghost subscription mechanism as a backend to store the information collected via such a popup.

How does the Ghost subscription form work?

The HTML for a Ghost subscription form is very simple, consisting of only a single text input field and a submit button inside a standard form element.

<section class="subscribe-form">
    <h3 class="subscribe-form-title">Subscribe to Ghost</h3>
    <p class="subscribe-form-description">Get the latest posts delivered right to your inbox</p>
    <form data-members-form="subscribe">
        <div class="form-group">
            <input class="subscribe-email" data-members-email="" placeholder="[email protected]" autocomplete="false">
            <button class="button primary" type="submit">
                <span class="button-content">Subscribe</span>
                <span class="button-loader">
        <div class="message-success">
            <strong>Great!</strong> Check your inbox and click the link to confirm your subscription.
        <div class="message-error">
            Please enter a valid email address!

The contents of the <svg> element in line #10 are omitted for brevity – its just a loading icon that displays on the button while the form is being submitted.

Notice how simple this HTML is: the form element doesn’t contain an action attribute, nor does the sole input tag have a name.  So how does the form actually get submitted?

The answer is that Ghost automagically adds JavaScript to all pages that binds to known element attributes to certain actions.   When this code sees a form element with the attribute data-members-form=”subscribe”, a handler function is added to call the API endpoint:


When the user clicks ‘submit’, a JSON POST request containing the user email – specified by the data-members-email attribute – is send to the server.  If the POST request is successful, i.e. the server responds with a 200 status code, then the “message-success” div is shown by adding a “success” class to the form element. Otherwise, the “message-error” element is shown via a form “error” class.

The mechanism for associating actions to elements that contain known HTML data- attributes is the same for Bootstrap.

How will our exit-intent popup work?

We could call the send-magic-link API directly but instead we’ll let Ghost handle the details and instead watch the form element for whichever class Ghost adds.  If we see see the “success” class added to the form, we’ll know the form was submitted successfully and save the user’s email in local storage to avoid prompting them again.

When the user closes the popup, Bootstrap 4 emits a ‘hidden.bs.modal’ custom event.  We’ll listen for this event and check the form class.

Exit-intent popup implementation


The HTML consists of a single Bootstrap modal containing a Ghost subscription form.

<div class="modal fade subscribe-form" id="exitModal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-body p-5">
                <form data-members-form="subscribe">
                    <h5 class="form-header text-center mb-5">Subscribe to our mailing list.</h5>                    <div class="form-group mb-4">
                        <input class="form-control" data-members-email="" placeholder="[email protected]"
                    <div class="form-group">
                        <button class="btn btn-primary w-100 mx-0" type="submit">
                            <span class="button-content">Subscribe</span>
                            <span class="button-loader">{{> "icons/loader"}}</span>
                    <div class="subscribe-deny">
                        <a href="#" data-dismiss="modal">No, I don't want to join.</a>                    </div>                    <div class="message-success">
                            <strong>Great!</strong> Check your inbox and click the link to confirm your subscription.
                        <div class="mt-5">
                            <button data-dismiss="modal" class="btn btn-primary">OK</button>
                    <div class="message-error">
                        Please enter a valid email address!

Best practice is to put this HTML into a Handlebars partial of your Ghost theme so that it may be easily included wherever needed.


The JS changes extend the code from our previous post by calling addEventListener on the modal element.

<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap.native/3.0.0/bootstrap-native.min.js"></script>
    (function () {
        var modalId = 'exitModal';
        var modal = document.getElementById(modalId);
        function addEvent(obj, evt, fn) {
            if (obj.addEventListener) {
                obj.addEventListener(evt, fn, false);
            } else if (obj.attachEvent) {
                obj.attachEvent("on" + evt, fn);
            }        }        addEvent(document, 'mouseout', function (evt) {
            if (evt.toElement === null && evt.relatedTarget === null) {
                if (!sessionStorage.getItem('modal.' + modalId) && !localStorage.getItem('email')) {
                    var modalInitJS = new BSN.Modal('#' + modalId);
        modal.addEventListener('hidden.bs.modal', function (event) {
            var form = modal.getElementsByTagName('form')[0];
            if (form.classList.contains('success')) {
                var input = modal.querySelector('input[data-members-email]');
                var email = input.value;
                console.log(`Successfully registered user "${email}"`);
                localStorage.setItem('email', email);
            } else {
                console.log('User registration incomplete.');
            }            sessionStorage.setItem('modal.' + modalId, '1');
        }, false);

When the user dismisses the modal, the form element is checked for the “success” class and, if present, saves the user email in local storage.


The Ghost subscription API is a perfect backend for an exit-intent popup.  This post demonstrated how such a popup is implemented using Bootstrap and a handful of lines of JavaScript.  All completely free.  You’ll see everything described here in action as you exit this page :).

Subscribe for Email Updates

Everything an ambitious digital creator needs to know.