WPからGatsbyへ移行時に気を付けたいSEO対策一覧と導入方法
9分目次
- 概要
- まずは Helmet のインポート
- 必要な SEO 対策一覧
- 適切なマークアップ
- title、meta タグ
- canonical の設定
- 構造化データ
- robot.txt の設置
- サイトマップの設置
- Search コンソールでの登録
- まとめ
概要
WordPress から Gatby に移行するにあたり、重要なものの一つが SEO 対策です。通常、Gatsby では Helmet を導入せずにビルドするとtitle
タグやmeta
タグなどは書き出されません。
しかし、Gatsby にはHelmetという便利なプラグインが用意されており、インストールするだけでひとまずの SEO 対策ができちゃいます
もちろんそれだけでは足りないので本記事執筆に至ったわけですが……
まずは Helmet のインポート
多くの方が Gatsby ブログの構築に何らかの starter を使用しており、その場合は必ずと言っていいほど Helmet がすでにインストールされているかと思います。packages.json
ファイル内にてgatsby-plugin-react-helmet
と検索してみてください。
ヒットしていたら Helmet が使えるので、blog の template ファイルや layout コンポーネント等、各ファイルでimport
しておきましょう。
import Helmet from "react-helmet";
必要な SEO 対策一覧
SEO 対策として最低限必要なものを以下にまとめました。
- 適切なマークアップ(h1 はひとつ)
- title、meta タグ(description など)
- canonical(正しい URL を検索エンジンに伝える)
- 構造化データ(クローラーの最適化)
- robot.txt の設置(クローラーの最適化)
- Sitemap の生成(クローラーの最適化)
- Search コンソールでの登録
国産 WordPress テーマではこれらを自動的に出力してくれるようにしているものが多いです(Sango や Affinger、Jin などなど)。
これらが SEO 的に効果があるだとかないだとか必須だとかはここでは言及しません。あくまで Audits での SEO 項目が 100 点になる+α のために必要なものを書き出しました。
あとは画像にはalt
属性つけるだとかinput
にはlabel
だとか重箱の隅を突くようなものも多いですが、ここでは上で挙げた最低限のものをカバーできれば良しとしましょう 🙆
適切なマークアップ
トップページではタイトルにh1
を、それ以外のページではその他の項目にh1
を設定します(記事ページではタイトルなど)。トップページかどうかの判断にはlocation
を使いましょう。
Layout コンポーネントで header を呼び出す際に、
// ご自身の環境に合わせて適宜変更してください
class Layout extends React.Component {
render() {
const { title, location, children } = this.props;
return (
<div>
<Header title={title} location={location} /> {children}
<Footer />
</div>
);
}
}
とし、header に location を渡してあげて、
// ご自身の環境に合わせて適宜変更してください
const Header = ({ titile, location }) => {
rootPath = `${__PATH_PREFIX__}/`;
let headerTitle;
if (location.path === rootPath) {
headerTitle = <h1>{title}</h1>;
} else {
headerTitle = <h3>{title}</h3>;
}
return (
<HeaderTag>
<Link to="/">{headerTitle}</Link>
</HeaderTag>
);
};
こうすればロゴ(サイトタイトル)部分はトップページではh1
で出力され、それ以外ではh3
として出力されます 🎉
あとは記事テンプレート側でタイトルをh1
として出力してあげましょう。
title、meta タグ
こちらは、src
内に SEO コンポーネントを新しく作成するのがいいかと思います。
import React from "react";
import PropTypes from "prop-types";
import Helmet from "react-helmet";
import { StaticQuery, graphql } from "gatsby";
function SEO({ lang, title, meta, description, keywords }) {
return (
<StaticQuery
query={detailsQuery}
render={(data) => {
const metaDescription =
description || data.site.siteMetadata.description; //descriptionが設定されていなければサイトのdescriptionが表示される
return (
<Helmet
htmlAttributes={{
lang,
}}
title={title}
titleTemplate={`%s | ${data.site.siteMetadata.title}`}
meta={[
{
name: `description`,
content: metaDescription,
},
{
property: `og:title`,
content: title,
},
{
property: `og:description`,
content: metaDescription,
},
{
property: `og:type`,
content: `website`,
},
{
name: `twitter:card`,
content: `summary`,
},
{
name: `twitter:creator`,
content: data.site.siteMetadata.author,
},
{
name: `twitter:title`,
content: title,
},
{
name: `twitter:description`,
content: metaDescription,
},
]
.concat(
keywords.length > 0 //キーワードが設定されている時のみ表示
? {
name: `keywords`,
content: keywords.join(`, `),
}
: []
)
.concat(meta)}
/>
);
}}
/>
);
}
SEO.defaultProps = {
lang: `ja`,
meta: [],
keywords: [],
};
SEO.propTypes = {
description: PropTypes.string,
lang: PropTypes.string,
meta: PropTypes.array,
keywords: PropTypes.arrayOf(PropTypes.string),
title: PropTypes.string.isRequired,
};
export default SEO;
const detailsQuery = graphql`
query DefaultSEOQuery {
site {
siteMetadata {
title
description
author
}
}
}
`;
siteMetadata 内に author がない場合は、gatsby-config
ファイルから追加できます。あとは記事のテンプレートやindex.js
などから呼び出せば OK。Seo コンポーネントの import を忘れずに。
import Seo from 'src/components/Seo';
// 略
class BlogPostTemplate extends React.Component {
render() {
const { markdownRemark } = this.props.data;
const { frontmatter, html, parent } = markdownRemark;
const siteTitle = this.props.data.site.siteMetadata.title;
const siteUrl = this.props.data.site.siteMetadata.siteUrl;
return (
<Layout title={ siteTitle } location={ this.props.location }> <Seo title={ frontmatter.title } keywords={ frontmatter.keywords } description={ frontmatter.metaDescription }
/>
// 略
);
};
};
ちなみに僕は keyword をいちいち設定するのが面倒なのでタグとして登録したものを keywords としてしまっています。こんな感じ(keywords={ frontmatter.tags }
)ですね。
あとは記事を書くときに、
---
template: BlogPost
path: /hoge
date: 2020-04-22T00:00:00.000Z
title: 記事タイトル。h1として出力されます
metaDescription: metadescriptionに使用されます
tags: ["僕の場合は", "ここを", "keywords", "として", "使っています"]
keywords: ["その他の人は", "こんな感じ", "ですね"]
---
こんな感じで書き出せば OK ですね。
canonical の設定
これはHelmet
を使用します。
// 略
return(
<Layout title={ siteTitle } location={ this.props.location }>
<Seo
title="新着記事一覧"
keywords={["meta keywordです"]}
/>
<Helmet> <link rel="canonical" href={ siteUrl } /> </Helmet>
記事テンプレ側でも設定しましょう!こちらでも Helmet の import をお忘れなくー!
// 略
return (
<Layout title={ siteTitle } location={ this.props.location }>
<Seo
title={ frontmatter.title }
keywords={ frontmatter.tags }
description={ frontmatter.metaDescription }
/>
<Helmet> <link rel="canonical" href={ `${ siteUrl + this.props.location.pathname }` }
/>
</Helmet>
構造化データ
これに関しても新しいコンポーネントを作成したほうがいいでしょう。
import React from "react";
import Helmet from "react-helmet";
import { StaticQuery, graphql } from "gatsby";
const JsonLD = ({ title, description, date, url }) => {
return (
<StaticQuery
query={JsonLdPostQuery}
render={(data) => {
const { siteUrl, author, categories } = data.site.siteMetadata;
const publisher = {
"@type": "Organization",
name: author,
logo: {
"@type": "ImageObject",
url: `${siteUrl}/assets/logo.png`, // 適宜変更してください
height: 150,
width: 150,
},
};
const authorData = {
"@type": "Person",
name: author,
image: `${siteUrl}/assets/avatar.png`, // 適宜変更してください
};
const jsonLd = {
"@context": "http://schema.org",
"@type": "BlogPosting",
mainEntityOfPage: url,
headline: title,
image: `${siteUrl}/assets/ogp.png`, // 適宜変更してください
editor: author,
url: url,
datePublished: date,
dateCreated: date,
dateModified: date,
description: description,
author: authorData,
publisher,
};
return (
<Helmet>
<script type="application/ls+json">{JSON.stringify(jsonLd)}</script>
</Helmet>
);
}}
/>
);
};
export default JsonLD;
const JsonLdPostQuery = graphql`
query JsonLdPostQuery {
site {
siteMetadata {
siteUrl
author
}
}
}
`;
コンポーネントが作成できたら、記事テンプレート側から呼び出します。
class BlogPostTemplate extends React.Component {
render() {
const { markdownRemark } = this.props.data;
const { frontmatter, html, parent } = markdownRemark;
const siteTitle = this.props.data.site.siteMetadata.title;
return (
<Layout title={ siteTitle } location={ this.props.location }>
<Seo
title={ frontmatter.title }
keywords={ frontmatter.tags }
description={ frontmatter.metaDescription }
/>
<Helmet>
<link
rel="canonical"
href={ `${ siteUrl + this.props.location.pathname }` }
/>
</Helmet>
<PostJsonLD title={ frontmatter.title } description={ frontmatter.description } date={ frontmatter.date } modifiedDate={ frontmatter.date } url={ this.props.location.href } />
構造化データをうまく出力できているかどうかはこちらのツールで確認できます。
robot.txt の設置
こちらにつきましてはgatsby-plugin-robots-txt
というプラグインにて簡単に行えます。
$ yarn add gatsby-plugin-robots-txt
OR
$ npm i gatsby-plugin-robots-txt
gatsby-config ファイルへの追記も忘れずに。
うまく robot.txt を設置できたかどうかのチェックツールはこちらのページの青いボタンから使用できますし、普通に domain.com/robots.txt にアクセスする手もあります。
参考: Gatsby 製のサイトに robots.txt を配置して検索サイトのクロールを制御する
サイトマップの設置
こちらについてもgatsby-plugin-sitemap
というプラグインがあるので利用していきましょう。
$ yarn add gatsby-plugin-sitemap
OR
$ npm i gatsby-plugin-sitemap
gatsby-config
に明記。ビルドすれば public 直下に sitemap.xml が生成されます。
Search コンソールでの登録
これは Gatsby に限らずどのサイトでも手順は同じなので参考リンクだけで詳細は割愛。
まとめ
とりあえず WP と同じ状態を再現できて大満足。