🦄

Next.jsでブログを作ってみました

2023/01/10に公開

概要


Next.js, react-markdown, react-syntax-highlighter, tailwindCSS, Figmaらを使って簡単な機能があるブログを作ってみました。
Vercelを使って公開しています。(※ サンプル記事はzennに書いた記事をいくつか入れているため、画像のurlにzennになっています。)

Blogはこちら

主なポイントは

  • DBは使わずマークダウンで記事を書くとhtmlに反映
  • 記事にtagをつけてtag一覧を生成
  • 記事のイメージをS3などの外部Storageは使わない(githubでマークダウンファイルを作って記事を直接書くと、イメージもアップロードしてURLが生成されるので)
  • 記事のサムネイルだけはpublic配下におく

機能一覧

最小限の機能を入れています。

  • マークダウン記事 → HTML表示
  • 記事内のcode blockに色付き
  • imageはimgタグを利用することでsize調節できるようにする
  • 目次表示
  • ロゴのアニメーション
  • サイドメニュー表示
  • profileページ作成
  • tag機能追加
  • レスポンシブ
  • SEO(meta tag, lighthouse, image lazy-load, robot.txt)

今後追加したいもの

  • dark mode
  • 共有機能
  • 検索機能
  • 記事の公開非公開機能
  • コメント機能
  • thumbnailジェネレータ
  • E2Eテスト

実装方法

マークダウン記事 → HTML表示

最初はremark-htmlとdangerouslySetInnerHTML使ってマークダウンをhtml化しましたが、react-markdownに変えました。
react-markdownの方が色んなプラグインをつけやすいし、全体的に簡単に書けたからです。

<ReactMarkdown
  rehypePlugins={[rehypeRaw]}
  components={CodeBlock}
  remarkPlugins={[gfm]}
  className="prose prose-stone mt-5 max-w-4xl m-auto"
>
  {post.content}
</ReactMarkdown>

rehype Pluginsで使いたいプラグインをnpm installして、rehypePlugins propsに追加します。
markdownの中でもHtmlタグが使えるように rehype-raw を追加しました。

rehypePlugins
rehype-raw

Tailwind CSS

CSSはTailwindCSSを使いました。
巣のCSSのように自由にデザインができるけど、簡単に統一したデザインができるし、個人的に使い慣れているからです。

マークダウンのスタイリング

< 前 >

< 後 >

マークダウンをhtmlにする時に、TailwindCSSのtypography-pluginを利用すると簡単にデザインが適応されます。
@tailwindcss/typographyをインストールしてmarkdownを囲んでいるクラスにproseとつけるだけです。

<article class="prose prose-xl">
  {{ markdown }}
</article>

上記の ReactMarkdown のclass名にも同じくつけています。

レスポンシブ

tailwind.config.js ファイルに画面サイズを定義して置いて、

  theme: {
    screens: {
      tablet: "640px",
      laptop: "1024px",
      desktop: "1280px",
    },
  }

classで tablet:grid のように利用します。以下の例は記事リストのカード配置のclassです。

 <div className="flex flex-col justify-center space-y-8 tablet:grid tablet:grid-cols-2 tablet:gap-2 tablet:space-y-0 desktop:grid-cols-3">

Responsive Design

記事内のcode blockのスタイリング

< 前 >

< 後 >

react-syntax-highlighter を利用しました。
react-markdownでコードをシンタックスハイライトさせるフログを参考にしました。

まずはcodeblockを整形するコンポーネントを作ります。

CodeBlock.tsx
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { a11yDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { CodeComponent } from "react-markdown/lib/ast-to-react";

const customCode: CodeComponent = ({ inline, className, children }) => {
  const style = a11yDark;
  const match = /language-(\w+)(:?.+)?/.exec(className || "");
  const lang = match && match[1] ? match[1] : "";
  const name = match && match[2] ? match[2].slice(1) : "";
  return !inline && match ? (
    <>
      {name && <span className="bg-stone-200 py-1 px-2 text-xs">{name}</span>}
      <SyntaxHighlighter
        style={style}
        language={lang}
        PreTag="div"
        className="md-codeblock"
      >
        {String(children).replace(/\n$/, "")}
      </SyntaxHighlighter>
    </>
  ) : (
    <code className="rounded-md bg-stone-200 text-red-600">{children}</code>
  );
};
export const CodeBlock = {
  code: customCode,
};

色んな highlighter styleの指定が可能です。私は a11yDark を選びました。
prism-themes

ReactMarkdown propsの components に作ったCodeBlockを追加するだけでOK

<ReactMarkdown
  rehypePlugins={[rehypeRaw]}
  components={CodeBlock}
  remarkPlugins={[gfm]}
  className="prose prose-stone mt-5 max-w-4xl m-auto"
>
  {post.content}
</ReactMarkdown>

ReactMarkdownのcomponentは特定のhtmlタグをカスタマイズすることができます。
例えばh1をh2にするとか、imgタグの中身を取得してリンクにするなどができます。
https://github.com/remarkjs/react-markdown#appendix-b-components

react-markdownから利用する方法

目次を作る

二つ方法を試しましたが、後者の方のrehype-tocを利用しました。

remark-tocを利用

最初は remark-tocを使って目次を作る方法です。導入はとても簡単でremark-tocrehype-slugをinstallし、
ReactMarkdownのremarkPluginsrehypePluginsにそれぞれ追加するだけです。
remark-tocはhtmlからtocを作ってくれるもので、rehype-slugはh1, h2のような見出しタグにidを付与して目次のリンクから各項目に飛べるようにしてくれます。

remark-toc

rehype-slug

$ npm install remark-toc
$ npm install rehype-slug
<ReactMarkdown
  rehypePlugins={[rehypeRaw, rehypeSlug]}
  components={CodeBlock}
  remarkPlugins={[gfm, [remarkToc, { maxDepth: 2, heading: "目次" }]]}
  remarkPlugins={[gfm]}
  className="prose prose-stone mt-5 max-w-4xl m-auto">
  {post.content}
</ReactMarkdown>

以下のように、マークダウンの中に「目次」と書いてあるところの下に作られます。(装飾は自分のでデザインに引っ張られているだけ。)

rehype-tocを利用

rehype-tocrehype-slugを使って目次を作る方法です。
同じくnpmでinstallし、今回は両方ともrehypePluginsに追加します。
rehype-toc

rehype-tocはより多くのオプションが指定できました。tocOptionsとして別途optionを作りました。
cssClassesオプションを使ってclass名をつけると、それを付与したtocが作られます。
これで目次のデザインも自由にできます。

const tocOptions = {
   headings: "h2",
   cssClasses: {
     toc: "prose-toc",
     list: "prose",
     listItem: "prose-toc-list-item",
     link: "prose-toc-link",
   },
 };

<ReactMarkdown
   rehypePlugins={[rehypeRaw, rehypeSlug, [rehypeToc, tocOptions]]} // rehypeTocとそのoptionを渡す
   components={CodeBlock}
   remarkPlugins={[gfm]}
   className="prose prose-stone mt-5 max-w-4xl m-auto"
 >
   {post.content}
 </ReactMarkdown>

作られる目次の中身

<nav className="prose-toc">
  <ol className="prose prose-1">
    <li className="prose-toc-list-item prose-toc-list-item-h2"><a
        className="prose-toc-link prose-toc-link-h2" href="#概要">概要</a></li>
    <li className="prose-toc-list-item prose-toc-list-item-h2"><a
        className="prose-toc-link prose-toc-link-h2" href="#state-of-css-2022">State of CSS 2022</a>
    </li>
    <li className="prose-toc-list-item prose-toc-list-item-h2"><a
        className="prose-toc-link prose-toc-link-h2" href="#まとめ">まとめ</a></li>
  </ol>

post.contentの一番上に自動で目次が作られます。

lighthouse / SEO

Chrome ブラウザの lighthouse を回して引っかかったものを追加しました。
meta tag、robot.txt, imageのlazyloadなど色々指摘してくれたので修正しました。


最後

ブログでもなんでもいいと思いますが、自分のWebpageを持っていると自由に遊べるテーマパークができたようで楽しいです。
もっと整ったらソースコードも公開して見ようと思います。

参考

Discussion