ふわり研究所

【Astro】 Astro Boilerplateにタグ一覧ページを実装してみる

By Selcia Eremeev on Jan 7, 2024
11

はじめに


このサイトのベーステーマには、Astro Boilerplateを使用しています。

このテーマは、記事にタグを付与する事は可能ですが、タグ一覧ページや、タグ別のページを表示する機能を装備していません。そのため、自分で調べて実装してみようと思います。

この機能の必要性について


例えば、何らかのキーワードで検索して、とあるサイトに訪れたとしましょう。例えば・・・、そうですね。キーワードは Astro にしましょう。

とあるサイトのAstroに関する記事を読んで、その人が執筆する別のAstroの記事が気になった場合、どのように検索をすれば良いでしょうか。検索方法はいくつかあります。

  • そのサイトの、記事一覧ページからキーワードでページ内検索を行い、記事を特定する
  • {キーワード} site:{そのサイトのURL} で、Google検索を行う

etc…

これらの方法はどれも読み手が行う必要があります。まあそうですよね。こちら側では何も用意していないんですから。

つまり、よりサイトを回游してもらうためには、タグ分けやカテゴリ分けは必要と考えます。

実装してみる


Astroの公式ドキュメントに、タグページの生成 について書かれているので、 これに則りながら、なるべく Astro Boilerplate が用意してくれているコンポーネントを使用して実装してみます。

  • 適当な記事に、タグを付与する

タグが付与された記事が無い事には何も始まりません。適当にタグを付与します。

---
layout: '@/templates/BasePost.astro'
title: 【Astro】 Astro Boilerplateにタグ一覧ページを実装してみる
description: 
pubDate: 2024-01-07T00:00:00Z
imgSrc: '/assets/images/11/11.avif'
imgAlt: '11'
+ tags: [Astro]
---
  • astro-boilerplate-components に用意されている BlogCard.tsx, BlogGallery.tsx を踏襲して、TagCard.tsx, TagGallery.tsxを実装する

私は下記のように実装してみました。色合いなどは、サイトに応じて調整して下さい。

src/partials/TagCard.tsx

type ITagCardProps = {
  tagName: string;
  imgSrc: string;
  imgAlt: string;
};

const TagCard = (props: ITagCardProps) => (
  <a className="hover:translate-y-1" href={`/tags/${encodeURIComponent(props.tagName)}/`}>
    <div className="overflow-hidden rounded-md bg-slate-800">
      <div className="aspect-w-3 aspect-h-2">
        <img
          className="h-full w-full object-cover object-center"
          src={props.imgSrc}
          alt={props.imgAlt}
          loading="lazy"
        />
      </div>

      <div className="px-3 pt-4 pb-6 text-center">
        <h2 className="text-xl font-semibold">
          {props.tagName}
        </h2>
      </div>
    </div>
  </a>
);

export { TagCard };

src/partials/TagGallery.tsx

import { TagCard } from './TagCard';

type ITagGalleryProps = {
  tagList: { tagName: string; imgSrc: string; imgAlt: string; }[];
};

const TagGallery = (props: ITagGalleryProps) => (
  <div className="tags">
    <div className="grid grid-cols-1 gap-6 md:grid-cols-3">
      {props.tagList.map((tag, index) => (
        <TagCard key={index} {...tag} />
      ))}
    </div>
  </div>
);

export { TagGallery };
  • タグ一覧ページを実装する

Astro Boilerplate の 記事一覧 (All Posts) ページと同じデザインになっています。

タグの文字だけだと寂しいかなと思い、記事と同じようにサムネイルを表示するようにしています。サムネイル画像は、/assets/images/tags/${tag}.avif のような形で配置するようにしています。

src/pages/tags/index.astro

---
import type {
  IFrontmatter,
} from 'astro-boilerplate-components';
import {
  PaginationHeader,
  Section,
} from 'astro-boilerplate-components';

import { TagGallery } from '@/partials/TagGallery';
import { CTA } from '@/partials/CTA';
import Base from '@/templates/Base.astro';
import { AppConfig } from '@/utils/AppConfig';

const allPosts = await Astro.glob<IFrontmatter>('../posts/*.md');
const tags = [...new Set(allPosts.filter((post) => post.frontmatter.tags !== undefined).map((post) => post.frontmatter.tags).flat())];

const tagList = Array.from(tags).map(tag => ({
  tagName: tag,
  imgSrc: `/assets/images/tags/${tag}.avif`,
  imgAlt: tag
}));

const title = `All Tags - ${AppConfig.site_name}`;
const description = `All Tags - ${AppConfig.site_name}`;
---

<Base head={{ title, description }}>
  <Section>
    <PaginationHeader
      title='All Tags'
      description=''
    />
  </Section>

  <Section>
    <TagGallery tagList={tagList} />
  </Section>

  <CTA />
</Base>
  • タグ毎の記事一覧ページを実装する

タグでフィルタした記事一覧ページです。

src/pages/tags/[tag].astro

---
import {
  BlogGallery,
  NewerOlderPagination,
  PaginationHeader,
  Section,
} from 'astro-boilerplate-components';

import Base from '@/templates/Base.astro';

import { CTA } from '@/partials/CTA';
import { AppConfig } from '@/utils/AppConfig';

export async function getStaticPaths() {
  const allPosts = await Astro.glob('../posts/*.md');
  const postsWithTags = allPosts.filter((post) => post.frontmatter.tags);
  const uniqueTags = [...new Set(postsWithTags.map((post) => post.frontmatter.tags).flat())];

  return uniqueTags.map((tag) => {
    const filteredPosts = postsWithTags.filter((post) => post.frontmatter.tags.includes(tag));
    return {
      params: { tag },
      props: { posts: filteredPosts },
    };
  });
}
const { tag } = Astro.params;
const { posts } = Astro.props;

const title = `${tag} - ${AppConfig.site_name}`;
const description = `${tag} - ${AppConfig.site_name}`;
---

<Base head={{ title, description }}>
  <Section>
    <PaginationHeader title={tag} description='' />
  </Section>

  <Section>
    <BlogGallery postList={posts} />
  </Section>

  <CTA />
</Base>

以上で完了です。このコードは、この記事執筆時点での私のサイトのタグ機能です。 ページ上部の Tags 以下が、このコードによって形成されています。ぜひ遷移してみて下さい。

しかし、タグ機能としてはまだ不完全です。記事に付与したタグ情報はタグページを生成するためだけにしか使用していないためです。

記事の上部、または最下部に、タグ情報を表示する事で、読者の回游に繋がると考えます。その機能はまた、モチベーションがある時にでも実装してみます。

Copyright © 2024 ふわり研究所