【Gatsby.js】個別ページをマークダウンで書きたい
前回:スターターを使ってブログ立ち上げまで
「このブログについて」みたいなページを作成しようと思います。
どうやら .js
ファイルで作成するのがよくあるケースのようなのですが、個人的には全ページ .md
ファイルに統一したいのでそんな感じにします。
環境
- Node.js:v16.16.0
- Gatsby, Gatsby CLI:4.20.0
- gatsby-starter-blog を使用
目次
1. ページ生成のためのテンプレート作成 ↑
ブログ記事と同じテンプレートを使ってしまうと「前の記事へ」「次の記事へ」が表示されるので、個別ページ専用のものを作成します。
まだよく分かっていないので(ひどい)、ブログ用のものをコピーして、使わなさそうなところを削除します。
src/templates/page.js
import * as React from "react" import { graphql } from "gatsby" import Layout from "../components/layout" import Seo from "../components/seo" const PageTemplate = ({ data: { site, markdownRemark: page }, location, }) => { const siteTitle = site.siteMetadata?.title || `Title` return ( <Layout location={location} title={siteTitle}> <article className="blog-post" itemScope itemType="http://schema.org/Article" > <header> <h1 itemProp="headline">{page.frontmatter.title}</h1> </header> <section dangerouslySetInnerHTML={{ __html: page.html }} itemProp="articleBody" /> </article> </Layout> ) } export const Head = ({ data: { markdownRemark: page } }) => { return ( <Seo title={page.frontmatter.title} description={page.frontmatter.description || page.excerpt} /> ) } export default PageTemplate export const pageQuery = graphql` query PageBySlug( $id: String! ) { site { siteMetadata { title } } markdownRemark(id: { eq: $id }) { id excerpt(pruneLength: 160) html frontmatter { title description } } } `
2. 個別ページの元になる、マークダウンファイルを作成 ↑
とりあえずふたつ作成します。
template
で使用するテンプレートを指定して、ブログ記事と出し分けします。既存のブログ記事(
content/blog
配下 )のfrontmatter
(---
で囲まれているところ ) にもtemplate: "post"
を追加しておきます。
content/pages/about.md
--- title: このブログについて description: "おはようございます" template: "page" --- ## おはようございます good morning ## こんにちは hello ## こんばんは good evening
content/pages/test.md
--- title: テスト description: "あいうえお" template: "page" --- ## あいうえお aiueo ## かきくけこ kakikukeko ## さしすせそ sashisuseso
content/blog 配下の全 .md
ファイル
- 例として
content/blog/hello-world/index.md
---
title: Hello World
date: "2015-05-01T22:12:03.284Z"
description: "Hello World"
+ template: "post"
---
This is my first post on my new fake blog! How exciting!
...
3. ページの生成 ↑
content/pages
配下も、ページ生成させる仲間に加えます。
gatsby-config.js
個別ページへのリンクは、
menu
で表示させます。取得できた順に表示だと任意の順番にできなさそうと思った &
frontmatter
にseq
のような値を持たせるのは嫌だというのが理由です。もうちょっとスマートな書き方はないのだろうか……。
module.exports = { siteMetadata: { title: `アルミ缶の上にあるミカン`, author: { name: `kyoruni`, summary: `ほげ`, }, ... + menu: [ + { + label: 'このブログについて', + path: '/pages/about' + }, + { + label: 'テスト', + path: '/pages/test' + } + ] }, plugins: [ `gatsby-plugin-image`, { resolve: `gatsby-source-filesystem`, options: { - name: `blog`, - path: `${__dirname}/content/blog`, + name: `pages`, + path: `${__dirname}/content`, }, }, ...
gatsby-node.js
frontmatter
のtemplate
がpage
かpost
かで、使うテンプレートを変更しています。allMarkdownRemark
のnode
がnext
previous
を持っていたので、最初はこちらを「次の記事 / 前の記事」に使おうと思っていたのですが、page
もpost
も含む全ページが対象になってしまったのでやめました。スターターに元から入っていた処理( 配列の1つ前が前の記事、1つ後ろが次の記事 )そのままにしています。
... // Define a template for blog post - const blogPost = path.resolve(`./src/templates/blog-post.js`) + const postTemplate = path.resolve(`./src/templates/blog-post.js`) + const pageTemplate = path.resolve(`./src/templates/page.js`) // Get all markdown blog posts sorted by date const result = await graphql( ... fields { slug } + frontmatter { + template + } } } } ... return } - const posts = result.data.allMarkdownRemark.nodes + const nodes = result.data.allMarkdownRemark.nodes + const posts = nodes.filter(node => node.frontmatter.template === 'post') + const pages = nodes.filter(node => node.frontmatter.template === 'page') ... if (posts.length > 0) { posts.forEach((post, index) => { const previousPostId = index === 0 ? null : posts[index - 1].id const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id createPage({ path: post.fields.slug, - component: blogPost, + component: postTemplate, context: { id: post.id, previousPostId, nextPostId, }, }) }) } + if (pages.length > 0) { + pages.forEach(page => { + createPage({ + path: page.fields.slug, + component: pageTemplate, + context: { + id: page.id, + }, + }) + }) + } } exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions if (node.internal.type === `MarkdownRemark`) { const value = createFilePath({ node, getNode }) createNodeField({ name: `slug`, node, value, }) } } exports.createSchemaCustomization = ({ actions }) => { const { createTypes } = actions ... createTypes(` ... type Frontmatter { title: String description: String date: Date @dateformat + template: String } type Fields { slug: String } `) }
4. メニュー表示用のコンポーネントを作成 ↑
とりあえず表示できれば良いので、雑にリストを作ります。
src/components/menu.js
import * as React from "react" import { Link } from "gatsby" import { useStaticQuery, graphql } from "gatsby" const Menu = () => { const data = useStaticQuery(graphql` query MenuQuery { site { siteMetadata { menu { label path } } } } `) let menuItems = [] data.site.siteMetadata.menu.forEach(menuItem => { menuItems.push(<li><Link to={menuItem.path}>{menuItem.label}</Link></li>) }) return ( <ul className="menu"> {menuItems} </ul> ) } export default Menu
5. トップページからリンクを繋げる ↑
個別ページへのリンクを繋げているだけの、
Menu
コンポーネントを設置しています。トップページの記事一覧には、
frontmatter
のtemplate
がpost
のページのみを表示します。
src/pages/index.js
import * as React from "react" import { Link, graphql } from "gatsby" import Bio from "../components/bio" import Layout from "../components/layout" import Seo from "../components/seo" + import Menu from "../components/menu" const BlogIndex = ({ data, location }) => { const siteTitle = data.site.siteMetadata?.title || `Title` ... return ( <Layout location={location} title={siteTitle}> <Bio /> + <Menu /> <p> No blog posts found. Add markdown posts to "content/blog" (or the ... return ( <Layout location={location} title={siteTitle}> <Bio /> + <Menu /> <ol style={{ listStyle: `none` }}> {posts.map(post => { const title = post.frontmatter.title || post.fields.slug ... export const pageQuery = graphql` query { site { siteMetadata { title } } - allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) { + allMarkdownRemark( + sort: { + fields: [frontmatter___date], + order: DESC + } + filter: { + frontmatter: { + template: { + eq: "post" + } + } + } + ) { ...
トップページ
個別ページ
できました!ヤッター
6. 参考 URL ↑
-
- 以前使っていたスターター:公式の Starter Library から消えてしまっていたのですが、そういえば個別ページをマークダウンで書いていたな〜と思ったので参考にさせていただきました
7. おわりに ↑
同じ感じでカテゴリーとかタグを追加できそうです!