Setting Up Private Email Newsletters with Ghost
It’s no big secret that we’re big fans of the Ghost publishing platform around here. One of the biggest advantages that Ghost offers over other content management systems (like WordPress, for example) is that it couples web publishing together with email newsletters in one easy to use package.
Essentially you can get the same thing that Substack offers, but while keeping ownership of all of your content and SEO benefits.
But the downside of this, is that you have to do some of the configuration on your own with Ghost. And some things just don’t work out of the box the way you would expect.
One example of this is setting up newsletters.
In order to set up a typical blog with an email newsletter, we would want to be able to:
- Differentiate between paid and free newsletter subscribers
- Send new blog posts directly to subscribers by email
- Send newsletters that don’t show up on our site as blog posts
- Have an archive page where somebody can browse all of the past newsletters
Ghost does the first two of these things right out of the box with no problem.
When you are ready to publish a post as a newsletter you get this popup from Ghost:
This gives us exactly what we want when it comes to sending a new post directly to our members. It simply sends the post out by email, and gives us control over who receives it.
But what if we don’t want to send blog posts in email form, but instead want to create a standalone newsletter? This is where we run into a few problems with the default Ghost setup.
Let’s see why and how we can fix them.
Sending an Email Newsletter with Ghost
When we want to build a private newsletter in Ghost, we need to start by creating it the same way we would any other Post.
So we will build out different sections and put links directly in the body of our Post content:
And everything still looks fine when we press publish and send it out. Our newsletter gets delivered to the segment that we’ve specified and the default formatting comes through pretty clean.
The issue is that we have now published this newsletter as a post to our website and it will show up on our homepage along with our other posts. This is because Ghost by default assumes we want to send out our posts as emails (like Substack), which causes problems if we want something to be treated as a newsletter only.
This is something Ghost is working on and they have some features in testing that might make this a simple fix, but for now there’s already a proven way to solve this problem - it just takes a bit of configuration.
Ghost Routes
The secret lies in Ghost’s routes.yaml
file. Here you are able to specify Ghost collections, which are essentially groupings for your posts. You can access it by going to your Ghost Dashboard > Settings > Labs > Routes > Download current routes.yaml
By default, the routes.yaml
file looks like this:
## routes.yaml
routes:
collections:
/:
permalink: /{slug}/
template: index
taxonomies:
tag: /tag/{slug}/
author: /author/{slug}/
We won’t be dealing with the routes
or taxonomies
sections for the purposes of this article, so don’t worry about those for now.
If you look under collections, we see the character /
, which means the home page of our website. So this means that when someone visits our homepage, Ghost will pull this collection of blog posts to display there. Since we don’t have any filters set by default, Ghost will pull all posts and serve them.
After that, we see permalink
, which tells Ghost how to create the urls for all of the posts in this collection. By default it reads /{slug}/
which means that every post will look like this: mydomain.com/slug
.
Finally, we see template
which is sent to index
. This means that when someone visits the homepage, they are served the default index page, which in Ghost’s default Casper theme looks something like this:
So to sum up what’s going on:
- Ghost is pulling all posts and putting them into a collection
- That collection is being served at the homepage (
mydomain.com/
) - All posts are created with a url that reads
mydomain.com/slug
- Ghost is displaying these posts with links to them using the default index page that comes with Casper or whichever theme you are using
Configuring Routes for Newsletters
Now that we understand how the routes.yaml
file works, how do we configure it for our newsletter? We basically need to do two things:
- We need to filter out newsletter posts from our default collection
- Then we need to create a new collection for newsletter posts
So let’s go back to our routes.yaml
and change the collections section it so it looks like this:
collections:
/:
permalink: /{slug}/
template: index
filter: tag:-hash-newsletter
In our homepage route /
we have added a filter tag:-hash-newsletter.
This is telling Ghost to remove any post with the tag #newsletter from the collection. (the “-” in front means remove)
Note that we are using what’s called a Private Tag in Ghost - essentially, any tag that starts with # is used for internal purposes only and won’t show up on a published post.
Now once we update our new routes.yaml
file, we won’t have our newsletters showing up on our homepage as long we add the tag #newsletter to our Post settings.
One more thing to keep in mind is that depending on the theme you are using, you might be showing posts in different places besides just your homepage. So for example, in the default Casper theme, you will see related posts at the bottom of every post.
In order to prevent our newsletters from showing up here, we need to go into our theme and edit the file post.hbs
.
We need to find the section that looks like this:
{{!-- Read more links, just above the footer --}}
<aside class="read-more-wrap">
<div class="read-more inner">
{{!-- The {#get} helper below fetches some of the latest posts here
so that people have something else to read when they finish this one.
This query gets the latest 3 posts on the site, but adds a filter to
exclude the post we're currently on from being included. --}}
{{#get "posts" filter="id:-{{id}}" include="authors" limit="3" as |more_posts|}}
{{#if more_posts}}
{{#foreach more_posts}}
{{> "post-card"}}
{{/foreach}}
{{/if}}
{{/get}}
</div>
</aside>
The part we are interested in is where it says: filter="id:-{{id}}"
This default filter just keeps the current post from being shown as a recommended post for further reading.
We just need to update that to: filter="primary_tag:-#newsletter+id:-{{id}}"
This now tells it to exclude not only the current post, but also all posts that have the tag #newsletter.
Creating a Ghost Newsletter Archive
Finally, we just need to create our page for our newsletter archive. So let’s go back into our routes.yaml
file and update the collections section to the following:
collections:
/:
permalink: /{slug}/
template: index
filter: tag:-hash-newsletter
/newsletter/:
permalink: /newsletter/{slug}/
template: newsletter
filter: tag:hash-newsletter
We are not changing the /
collection at all, instead we have added a new collection called /newsletter/
. This means that when we navigate to mydomain.com/newsletter
, Ghost will pull a new collection of posts.
Since we have the filter tag:hash-newsletter
, this collection will only include posts that have the tag #newsletter. Our permalink
field here means that we will generate the links for all of our archived newsletters at mydomain.com/newsletter/slug
.
We are then specifying that we will be using a template called newsletter
, which we haven’t created yet. The reason for this is that we want to use a slightly different layout for our newsletter archive page compared to our homepage.
To do that, we will have to go into our theme and create a new file called newsletter.hbs
.
In the default Casper theme, we will probably want to remove the big hero image that looks out of place on any page but the homepage. So we will copy and paste just the following part from the default index.hbs
file into our new newsletter.hbs file
:
{{!-- The main content area --}}
<main id="site-main" class="site-main outer">
<div class="inner posts">
<div class="post-feed">
{{#foreach posts}}
{{!-- The tag below includes the markup for each post - partials/post-card.hbs --}}
{{> "post-card"}}
{{/foreach}}
</div>
</div>
</main>
And that’s it, now we have our newsletter archive page without the hero image from our homepage.
If you’re a bit lost when it comes to how you should update your theme, feel free to contact us. We do occasional theme development work and if you are a non-profit local news organization we may be able to take on a small project pro-bono.
Conclusion
With that you should now be able to set up your Ghost newsletters so you can:
- Send emails to paid and free members
- Send blog posts by email
- Create private email newsletters that don’t show up as posts
- Display all of your private newsletters in a newsletter archive