Making your blog smarter - How to implement suggestions
Tags: typescript, nextjs, ux, react
Published: 2019-10-08 16:22:15

One of the most effective methods for promoting your business is content creation.

You can choose between various methods and mediums through which to publish your insights.

More often than not, you'll want to choose something simple that can get you going immediately, such as [Medium](https://www.medium.com).

However, there's one advantage to building the blog yourself, and that is it allows you to customize the user experience.

One of the critical elements that can define your readers' experience is how they discover your content.

To do that, you must be able to suggest relevant content for your readers.
You want your readers to be fully engaged and benefit to the maximum from what you are offering.

In our [previous blog post](https://techhive.io/our-insights/making-your-blog-smarter-how-to-implement-suggestions), we have explored how we can build a powerful blog with __NextJs__ and __Contentful__, as well as how to integrate Google analytics and to enhance your meta tags.

In this piece, we explore how we can drive the blog forward by maximizing the value and knowledge ingested by our readers. We also do not want our readers to drop-off after reading one insight or article only.

This is why, in addition to creating great content, we enhance the user experience. To do so, we make suggestions at the end of every blog post to keep the user engaged.

## User Experience
When reading a blog post, the reader is usually researching information that would enrich his know-how and his knowledge.

The user also, more often than that, is interested in knowing more about the topic he is researching and is directly __related__ in content and essence to the article he is currently reading.

So the logical and straightforward way to suggest related content is by filtering our blog posts by tags and then displaying them.

## Prerequisite

In this post, we are not walking through the basics of building the blog. This post will, however, expand on the logic that we have already implemented in our previous article.

So if you didn't go through our previous blog post, you should do so before moving to this one.

Let's get started!

The blog we've built utilizes the power of tags fully. We have already labelled every blog post with a set of tags that we define in our headless CMS.

If you haven't already, I highly encourage you to do so before resuming this article.

## Fetching the Suggestions

To fetch the suggestions, let's modify our `contentful.ts` file to include our newest function:

For our use case, we want to fetch three suggestions per post and display them at the end of the article.

Contentful provides us with many efficient ways to query our posts; in our case, by tags.

As an important note, __you want to make sure to exclude from your suggestions the current post you are reading__.

### In theory

Let's explore how our algorithm works:
```
1- User is reading blog post 1
2- We defined an empty suggestions set
3- Fetch blog post 1 tags on load
4- Find 3 blog posts with at least one matching tag
5- Append your result to the suggestions set
6- If you found less than 3 blog posts
7- Then, fetch (3 - result.length) random blog posts
8- Append to suggestions set
9- Return suggestions
```
Step number 7 is essential, as we do not want more than 3 suggestions per post.

### In implementation

To fetch each post's suggestions:

{% gist https://gist.github.com/abedzantout/8a14b5ebdc5266f15db494524abd9803.js
%}


Many things are happening above.

Remember that in our previous article, we've already included filtering by tags.

Our current goal is to find __at least one matching tag__ across all our articles and display those.

```
'fields.tags.sys.id[in]': tags.length ? tags.join(',') : undefined
```

The `[in]` allows us to do so, but since __Contentful__ allows us to query by reference in our case, we need to include the ids of the tags that Contentful generates. Those can be found until the __`fields.tags.sys`__ field.

What happens if we have less than 3 posts to display in the suggestions? 

In other words, if the blog post is not related to any other subject you have previously published, or you do not have enough content to display, then it is initially safe to suggest a random set of posts that the user could browse.

In this case, we query our entire posts collection randomly and append them to the original array.

Let's start off by excluding the current article (the one we want to fetch the suggestions for), from being fetched again:

```
'fields.slug[ne]': currentArticleSlug, // exclude current article
```

You also want to exclude fetching suggestions that you already fetched by tag.

```
'fields.slug[nin]': slugsToExclude, // exclude slugs already fetched
```

Finally, we append the random posts with the posts that we fetched by suggestions:

```
entries = [...entries, ...randomSuggestions.items];
```

### Bonus
We include a helper function to map the data to fit out __BlogPost__ interface

```
/**
 * Maps the items fetched by contentful
 * @param entries
 */
private mapData(entries): BlogPost[] {
    return entries.map(({sys, fields}: { sys: any; fields: any }) => ({
        id: sys.id,
        title: fields.title,
        description: fields.description,
        heroImage: fields.heroImage.fields.file.url,
        slug: fields.slug,
        tags: fields.tags,
        publishedAt: fields.publishDate
            ? new Date(fields.publishDate)
            : new Date(sys.createdAt),
    }));
}
```

## Displaying the suggestions
Let's modify our `pages/post/index.tsx` to fetch and display the suggestions.

Let's start first by fetching the data before the view is initialized, i.e: __`getInitialProps`__:

```
PostPage.getInitialProps = async ({ query }) => {
  const contentfulService = new ContentfulService();

  const { post } = query;
  const article = await contentfulService.getPostBySlug(post);
  // map the tags that we have fetched from Contentful
  const tags = article.tags ? article.tags.map(tag => tag.sys.id) : [];
  // pass the tags to the function we have built
  const suggestedArticles = await contentfulService.fetchSuggestions(
    tags,
    article.slug
  );

  return { article, suggestedArticles };
};
```

Now that the component is receiving the suggestions, we can now display them:

```
// helper function to display the cards
const renderCards = suggestions =>
  suggestions.map((suggestion, index) => (
    <Card key={index} info={suggestion} />
  ));
const PostPage: NextPage = (props: Props) => {
...
   return (
...
<div className="suggestions">{renderCards(props.suggestedArticles)}</div>
...
   );
};

```

The final step is to fix how the cards are displayed. Under `pages/post/styles.css`, add the following:

```
...
.suggestions {
  display: flex;
  flex-flow: row;
  padding: 1.5rem 0;
  margin: 1rem auto 1rem 4rem;
  justify-content: flex-start;
}

.suggestions .card {
  margin: 0 1rem;
  height: 450px;
  width: 320px;
}

.suggestions .card:first-of-type {
  margin-left: 0;
}
```
## Final words
Congratulations! You have successfully integrated suggestions into your __NextJs__ Blog and powered by __Contentful__ while utilizing the power of static websites!

---

## Ready-made Starter Kit
To make things easier for you, we have created a [starter kit](https://github.com/techhiveIO/nextjs-static-starter-kit "NextJs Static Starter Kit") that includes a template and can get you bootstrapped in no time. We even created a script to create your Contentful Content Data automatically and includes everything you have learned so far in building a great Blog.

Feel free to submit your PRs, issues and don't forget to star our repo ⭐.