DEV Community

Joshua Nussbaum
Joshua Nussbaum

Posted on

Create a blog with Sapper & Markdown: Tagging

When dealing with lots of content, it's a good idea to have some categorization. One versatile approach to categorization is tagging. Nearly all blogs use it, and ours will too.

Note: If you prefer to watch rather than read, there's a screencast version here:

Adding metadata

Continuing from Part 1, our blog repo has a /posts folder filled with Markdown files. Our first step will be to add a new field called tags to the "front matter" metadata in each markdown file.

title: Example Post
date: 2020-10-28
+tags: hello, world

# Example Title

Enter fullscreen mode Exit fullscreen mode

Parsing tags

Sine the tags string is comma-delimited, it will need to be converted into to an array during the parsing phase.

// in src/posts.js
// transform is called once for each post
function transform({metadata}) {

  // the `tags` field is optional, so default to empty list
  let tags = []

  // check if `tags` field is defined on this post
  if (metadata.tags) {
    // split the tags string by comma, and trim away any extra spaces
    tags = metadata.tags.split(',').map(str => str.trim())

  // return previous data and tags 
  return {...metadata, tags}
Enter fullscreen mode Exit fullscreen mode

Listing tags

Now that each post has a cleaned up tags array. It's ready to be displayed on the /post/:permalink page.

<!-- src/routes/posts/[permalink].svelte -->


<!-- new component to show tag list -->
+<Tags tags={post.tags}/>

{@html post.html}
Enter fullscreen mode Exit fullscreen mode

And the new <Tags/> component will be defined as:

<!-- src/components/Tags.svelte -->
  // take in tags as a prop
  export let tags

  <!-- iterate through each tag -->
  {#each tags as tag}
    <!-- link each tag to /tag/:tag page -->
    <a href="/tag/{tag}">#{tag}</a>
Enter fullscreen mode Exit fullscreen mode

Listing posts by tag

To show all the posts for a given tag, we'll need a new page and some JavaScript logic to filter posts by tag.

First, lets define the findByTag() function. It will take the tag as a parameter and return the list of posts matching the tag.

// src/posts.js
export function findByTag(tag) {
  // filter out all posts that don't include the tag
  return posts.filter(post => !post.tags.includes(tag))
Enter fullscreen mode Exit fullscreen mode

Then, define new page for /tag/:tag that will use findByTag() to locate posts:

<!-- src/routes/tag/[tag].svelte -->
<script context="module">
  import { findByTag } from '@/posts'

  export function preload(page) {
    // extract tag param
    const { tag } = page.params
    // find posts based on tag param
    const posts = findByTag(tag)

    // return props
    return { tag, posts }

  // props are provided by preload() function
  export let tag, posts

<!-- show #tag as title -->

{#each posts as post}
  <!-- show each post -->
Enter fullscreen mode Exit fullscreen mode

Extracting a component

Lastly, since we are showing the list of posts on 2 pages /tag/:tag and /post/:permalink, it would be to avoid duplication and have a reusable component for listing posts. This will make it easier to adjust and style down the road.

<!-- src/components/PostList.svelte -->
  // it takes an array of posts as a prop
  export let posts

<!-- iterate thru each post and output an <article> tag -->
{#each posts as post}
    <!-- link to /posts/:permalink page -->
    <a href={`/posts/${post.permalink}`}>
Enter fullscreen mode Exit fullscreen mode

And then update the pages src/routes/posts/[permalink].svelte and src/routes/tag/[tag].svelte to use <PostList/> instead of {#each}.

<!-- use our new shiny component -->
<PostList {posts}/>
Enter fullscreen mode Exit fullscreen mode


Our little blog is getting better! Now that we have tagging working, in the next post we'll explore adding syntax highlighting.

You can find all the code here:

Happy coding! ✌


Top comments (0)