List your Astro Posts by Tag

Technique to list posts (*.md) based on a selected tag.

astrotagsgetStaticPathsastro-beta
Published 2022-04-04

Introduction

An expected feature of a Blog site, is to have some way to list posts by selecting a tag, which is a keyword or concept that is talked about often or in several posts. This post you are reading has been tagged with: {frontmatter.tagsAlt}.

Demo

Click here to go to all posts tagged astro. You can click on each post’s tag-list to switch to that tag.

Page Code

Here is the whole page code that works on v0.26.0+, including beta.

/*  /src/pages/blog/tag-post/[tag].astro  */
---
import BaseLayout from './../../../layouts/BaseLayout.astro';

export async function getStaticPaths () {
  // grab all .md files from a folder
  let allPosts = await Astro.glob('./../*.md');
  // I need this filter to ignore files that do not have 
  // a title, or no published date (in frontmatter)
  // this way I leave the date out while the post is not finished.
  let filteredPosts = 
      allPosts.filter((p) => { 
        return p.frontmatter.title && p.frontmatter.published; 
      });
  // an object to collect existing tags
  let dix = {};
  filteredPosts.forEach((post) => {
    // go thru each tag in each published post,
    // fill in the object...
    post.frontmatter.tags.forEach((tag) => {
      if (!dix[tag]) dix[tag] = 0;
      dix[tag] += 1;
    });
  });
  console.log('dix', dix); // <-- prints out our tag-cloud data.

  let ary = [];
  // format the data to return from getStaticPaths()
  for (let prop in dix) {
    ary.push({
      params: { tag: prop.replace(/:/g, '') },
      props: { tagname: prop, count: dix[prop] }
    });
  }
  return ary;
}

// grab the selected tag from the url (dynamic [tag])
const tagname = Astro.params.tag;
const tagcount = Astro.props.count;
// These section feels duplicated from getStaticPaths()
// opportunity to refactor...
let allPosts = await Astro.glob('./../*.md');
let filteredPosts = 
    allPosts.filter((p) => { 
      return p.frontmatter.title && p.frontmatter.published; 
    });
// now filter to only posts containing the selected tag,
// these will be rendered below in the markup.
let tagged = filteredPosts.filter((p) => { 
  return p.frontmatter.tags.includes(tagname); 
});

---
<BaseLayout title="The Blog - tag list example" description="Astro Tag List Example for Blog">
  <h2><i class="fas fa-tag"></i> {tagname} - {tagcount} {tagcount>1 ? 'posts' : 'post'}</h2>
  {tagged.map(post => (
    <a href={post.url}>
      <div class="xpost-item box">
        <img src={post.frontmatter.og.basic.image} height="140" class="float-start me-3" />
        <h4>{post.frontmatter.title}</h4>
        <p>{post.frontmatter.summary}</p>
        {post.frontmatter.tags.map(tag => (
          <a href={`/blog/tag-post/${tag}`}><span class="badge bg-secondary me-1 mb-2">{tag}</span></a>
        ))}
      </div>
    </a>
  ))}
</BaseLayout>

<style is:inline>
.xpost-item { border:1px solid #000; padding: 0 15px 0 0; margin:10px 0; border-radius:10px; overflow:auto; }
a { text-decoration:none; }
.xpost-item:hover { border:1px solid #f72; }
</style>

Conclusion

I included /*commentary*/ through-out the code.



Back to Post List
ok!