New tutorial: How to create a read-next section

Whether it’s compulsively reading the next chapter, binge-watching a show, or impatiently waiting for the sequel — when something’s good, it’s hard to wait. Your readers feel the same way about your content, so why not give them more by suggesting what to read next?

In this tutorial, learn how to build and customize a read-next section for your Ghost theme. This feature can take various forms, and you’ll learn how to build four of the most useful versions:

  1. A read-next section that suggests the latest three posts
  2. A read-next section that suggests the latest three posts by the same author
  3. A read-next section that suggests the latest posts related by tag
  4. A read-next section that dynamically suggests content based on member status

No matter which version you build, a read-next section means your audience will never be without their next read :books:


Nice tutorial.

In step #4, I think your two ‘get’ statements are reversed? You should need to go up a context level in the INNER one, not the outer one, right?

I just did a related posts block for a client this week who has 10k posts, and about 10k tags! Some tags have 8k posts, and some have one. Selecting posts that matched post.tags turned out to be basically useless, because it’d pick up the common tag, never the rare and specific tag.

I wrote two solutions. The first one used handlebars, sorted tags, and used the rarest tags. It was clunky but worked on my demo site (which has only a couple hundred posts). We deployed it, and it didn’t work on the client site on Ghost Pro. No clue why, without access to the logs, and his export doesn’t want to import on my demo site (10k posts, ugh).

I rewrote in javascript. Works great, gets the most specific matches.

Here’s a snippet:

    let taglist =[]
    let threeposts = []
    let postid = '{{id}}'
    let nodupeslist=[]

    for (let el of ['{{tags.[0].slug}}','{{tags.[1].slug}}', '{{tags.[2].slug}}','{{tags.[3].slug}}','{{tags.[4].slug}}','{{tags.[5].slug}}','{{tags.[6].slug}}','{{tags.[11].slug}}','{{tags.[7].slug}}','{{tags.[8].slug}}','{{tags.[9].slug}}','{{tags.[10].slug}}','{{tags.[12].slug}}'] ) {
        if (el && el[0] != "#") {taglist.push(el)}

// pretty sure there's a better way to do that, but not sure what it is.

async function getRelated() {
    let data = await fetch(`${apistring}ghost/api/content/tags/?key=${apikey}&filter=slug:[${taglist}]&include=count.posts&order=count.posts%20ASC`)
    let tags = await data.json()
    let filterstring=""
    for (let onetag of tags.tags) {
            if (nodupeslist.length > 0 ) {filterstring=`%2Btags:-[${nodupeslist}]`}
            if (threeposts.length < 3) {
                let posts = await fetch(`${apistring}ghost/api/content/posts/?key=${apikey}&filter=id:-[${postid}]%2Btags:[${onetag.slug}]${filterstring}&limit=3`)
                    let dataposts = await posts.json()
                    for (let onepost of dataposts.posts) {
                        if (threeposts.length < 3 ) {
1 Like

You’re right. These were initially reversed, and I forgot to update the filters. Thank you :smile:

That’s a lot of posts! Cool, you found a solution.

Hi @RyanF ,
With Casper, this filter does not seem to work. If I add it, it shows nothing.

{{#get "posts" include="authors" filter="tags:[{{post.tags}}]+id:-{{}}" limit="3" as |more_posts|}}

Can you show more context around where you added it, @Joan ? And you’re adding it to the theme, not as code injection, right?

Is there anything in your Ghost error log?

Hi @Cathy_Sarisky , thanks for the reply,
I added on “post.hbs”. Look the image.

If I change the default:

{{#get "posts" filter="id:-{{id}}" limit="3" as |more_posts|}}


{{#get "posts" include="authors" filter="tags:[{{post.tags}}]+id:-{{}}" limit="3" as |more_posts|}}

The section disappears directly. If you can test it… It’s strange.

Is this happening within the posts context? If you put a TAGS {{post.tags}} END just before your ‘get’ statement, do you see a list of tags, or is it blank? I’m guessing blank. Depending on how you’re calling it, you might need {{tags}} instead. I see that your working example is using {{id}}, not {{}}…

Yes. if I put {{tags}} and {{id}} just before to “get”, it’s shows tags and the ID of the post. Good.
I tried to reemplace for:

{{#get "posts" include="authors" filter="tags:[{{tags}}]+id:-{{id}}" limit="3" as |more_posts|}}

But doesn’t work too. Not shows nothing.

Hmm. And you’re positive that your post that you’re testing with actually has a tag that’s also on another post, right? :) Sorry if that’s a silly question, but I’ve definitely made that mistake!

Yes yes! :sweat_smile: It’s strange… Let’s see if @RyanF answers, or someone else can try it with the Casper theme.

@Cathy_Sarisky pointed you in the right direction here. You either need to move the related posts block outside the post context or change the code.

To change the code, you’d need to use this:

    {{#get "posts" filter="id:-{{id}}+tags:[{{tags.[*].slug}}]" limit="3" as |more_posts|}}

This tells Ghost to output the slug for each tag.

Figuring this out can be tricky. This is a case where starting Ghost in logging mode can be a huge help because it’ll let you know if your filter has issues. So, instead of starting Ghost locally with ghost start, you’d use:

ghost run -D
1 Like

Thanks @RyanF ! works perfectly!

Is there any way to tell it to show the 3 articles following the Post where it is shown? Not the last 3 articles. Since in all articles with that Tag, the same ones are shown. am I understood?
For example: The post with ID 2222222, that in the Read Next section show the 3 articles that follow that ID.
Too, It would be nice, to show 3 random articles, but I have read in other threads, that this is not possible.

1 Like

I know it does no good to whine about “but in Wordpress …” – but I’ll whine a bit anyway. :slight_smile:

I had a WP plugin that, instead of doing related posts off tags, allowed you to choose the related posts you wanted through a search. I’d enter any word in the title of the post(s) I wanted, and once found, just check the ones I wanted listed. Resulting code was very nice-looking, and even better, was actually relevant to the story I placed them in. AND, it linked the current article back to those articles as well, if I told it to.

This is one of those times that I wish there was some way to post WP plugins to Ghost – or to reverse-engineer them into a site. But, I know that’s not possible – and not the direction the Ghost staff wants to go.

Okay, whine over. I’ll get some cheese to go with it. ;-)

1 Like

You could add a date filter here to say something like “show me related posts that were published after this one.” There are a lot of different filter options:

Ghost also has a previous/next post helper, which is even simpler to use:

Finally, you’re right that random posts aren’t possible at the theme level. However, you could do it with JS and the Content API. It’d be a bit more complex but doable!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.