Create a Dynamic CTA in Ghost
Tags: ghost, webdev
Published: 2022-01-31 18:16:06

---
title: Create a Dynamic CTA in Ghost
published: true
date: 2022-01-31 15:00:00 UTC
tags: Ghost,WebDev
canonical_url: https://ryanfeigenbaum.com/dynamic-cta/
---

 ![Create a Dynamic CTA in Ghost](https://ryanfeigenbaum.com/content/images/2022/01/mehdi-messrro-r-o95az6wvi-unsplash-1920.webp)

Getting someone to do something is _hard_. Asking them to type their email or to click a button isn't a foregone conclusion, you need to show them why the effort is worthwhile.

> Clicking is hardwork 😅

But as a creator, it's also hard to come up with effective CTAs.

With [Ghost's](https://ghost.org) templating layer, you can create more than your run-of-the-mill CTA by generating dynamic copy to increase engagement. Below, I'll show you how to easily create these dynamic CTAs in your post template.

## The Basic CTA

We'll start with a simple CTA that asks our readers to sign up for a free newsletter.

Ghost provides some [template magic](https://ghost.org/docs/themes/members/) we can use to add a signup form to any page. By adding the `data-members-form` attribute to a `form` element, we can tell Ghost to capture the subscriber's email and initiate a signup flow.

To make this work, you also need to add the `data-members-email` attribute to an input element and include a button to submit the form.

```html
<p>Your life is missing something...</p>

<p>Sign up for our free newsletter and get the goodness right in your inbox.</p>

<form class="form" data-members-form >
  <label>Email <input type="email" required data-members-email />
  </label>
  <button>
    <span class="loading">Loading...</span>
    <span class="default">Sign Up</span>
    <span class="success">Check Your Inbox!</span>
    <span class="error">Error! Try again</span>
  </button>
</form>
```

_Basic CTA_

Ghost's template magic also automatically adds state to your form by toggling classes on the form element. These classes will be merged with existing classes and removed when state is updated.

- `<form class="loading"...`: added after the user clicks submit
- `<form class="success"...`: added when signup is successful
- `<form class="error"...`: added when signup is unsuccessful

Before moving on, we can make an immediate improvement to user experience. Right now, this CTA will be present wherever it's added to a theme. That means a user will see it, even if they're already a member, which can be confusing—why am I seeing this signup form if I'm already a member?

By using the `@member` object and the `{{#if}}` helper, you can control when the signup form is displayed. The caret (^) in front of `if` in the code below inverts the if helper and means: show this content if the current user is _not_ a signed-in member. In other words, the CTA will only show to users who are not signed in.

```handlebars
{{^if @member}}
  
  <form class="form" data-members-form>
  	...
  </form>
    
{{/if}}
```

_Using the @member object_

### Styling the Sign Up Form

To show and hide the default, loading, success, and error states, you need to add a bit of CSS, as shown below.

In the markup above, state is indicated via text ("Loading..."), but you could just as easily use icons instead of or in addition to the text—the principle would be the same.

```css
/* Hide stateful content */
.form .loading, 
.form .success,
.form .error {
    display: none;
}

/* Show stateful content */
.loading .loading,
.success .success,
.error .error {
    display: initial;
}

/* Hide default when stateful content is shown */
.loading .default,
.success .default,
.error .default {
    display: none;
}
```

_Styling to hide and show state_

## The Dynamic CTA

Member data isn't all we have access to via the Ghost template layer. Within the context of a post, we have access to its data, which includes its title, tag, [and more](https://ghost.org/docs/themes/contexts/post/#post-object-attributes).

We can use this data to generate a dynamic CTA.

```handlebars
{{! Make sure you're in the post context}}
{{#post}}
  
  {{! Only show the CTA if user is not a signed-in user }}
  {{^if @member}}
    
    <p>What if you never had the chance to read <em>{{title}}</em> or my other articles about 
    <a href={{primary_tag.url}}>{{primary_tag.name}}</a>
    ?</p>
    
    <p>Avoid the possibility of your life getting much worse by subscribing to my newsletter. It's totally free.</p>
    
    <form class="form" data-members-form>
      <label>Email <input type="email" required data-members-email />
      </label>
      <button>
        <span class="loading">Loading...</span>
        <span class="default">Sign Up</span>
        <span class="success">Check Your Inbox!</span>
        <span class="error">Error! Try again</span>
      </button>
    </form>
  
  {{/if}}

{{/post}}
```

_Dynamic CTA_

While you'll need to find copy that'll suit your needs, the idea here is to update the CTA automatically based on the post's data. Instead of just saying, "Sign up for my wonderful newsletter," you can say, "Reading _Title of My Article_ was truly wonderful, wasn't it? Don't miss other posts about _x topic_ and sign up for my newsletter."

In the example above, I reference the article's title, its primary tag, and the link to that tag, so the reader can explore other posts on the same subject. Within the post object, you also have access to the post's author(s), description, publication date, excerpt, and more, all of which can be used to build creative CTAs to grow your audience.

Incorporating this data into a post's CTA is only a very small part of the battle of formulating an effective call to action. I hope, though, that having a few more tools at hand will spur your creativity in crafting kick-ass copy, and if I can have you take away anything from this tutorial, it's that your life will surely be much better if you [sign up for my newsletter](https://ryanfeigenbaum.com/#/portal/signup).