Next.jsでMDXを使ってみる

## 目次
1. MDXとは？
1. Next.jsで使ってみる
1. MDXのシンタックスをいろいろ試してみる
2. 所感
1. 余談


## MDXとは？
- 公式サイト: https://mdxjs.com/
- markdownとjsx両方を同時に書ける
- markdownはjsxをimportできる
- MDXはruntimeを持たない、ビルドで解決される

といった特徴があるようです。

```md:例
# Hello, *world*!

Below is an example of JSX embedded in Markdown. <br /> **Try and change
the background color!**

<div style={{ padding: '20px', backgroundColor: 'tomato' }}>
  <h3>This is JSX</h3>
</div>
```
(MDXは流石にシンタックスハイライトされない...)

ドキュメントやブログを書く際に便利ですね。

## Next.jsでMDXを使ったブログを作ってみる
https://mdxjs.com/getting-started/next 公式の通りに設定を入れると、あとはpages以下に好きなようにmdxファイルを作成するだけです。next devしましょう。

### markdownが吐き出すタグを変更する
MDXの中に書いたmarkdownの構文はjsxに変換されますが、どのコンポーネントに変換するかを設定することができます。
https://mdxjs.com/getting-started/#mdxprovider
公式サイトにある通り、MDXProviderを使います。今回はNext.jsなので_app.jsに書いてしまいます。
例として、markdownのcode blockにシンタックスハイライトを効かせます。
https://mdxjs.com/guides/syntax-highlighting
(最近でたnext/imageなんかをここで使うと良さそうですね)

```jsx:_app.js
import { MDXProvider } from "@mdx-js/react"
import { CodeBlock } from "../components/layouts/CodeBlock"

const mdComponents = {
  code: CodeBlock
}

const MyApp = ({ Component, pageProps }) => {
  return (
    <MDXProvider components={mdComponents}>
      <Component { ...pageProps } />
    </MDXProvider>
  )
}

export default MyApp
```

これでMDXに書いたcode blockはシンタックスハイライトが効くはずですね。
![スクリーンショット 2020-12-15 3.22.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42198/661a4f45-eb00-d020-6df7-ece8243af987.png)

### Layoutを定義してみる
MDXだと、特定のページにレイアウトを適用するのが非常に簡単です。
今回は例として、タイトルタグを設定するレイアウトを作成します。

```jsx:components/layout/BaseLayout.jsx
import Head from "next/head"

export const BaseLayout = ({ meta, children }) => (
  <>
    <Head>
      <title>{meta.title}</title>
    </Head>
    { children }
  </>
)
```

mdxでLayoutを適用します。

```mdx:.mdx
import { BaseLayout } from "../components/layouts/BaseLayout"

export const meta = {
  title: "ホーム"
}

# レイアウトを適用したいMDX

export default ({ children }) => (
  <BaseLayout meta={meta}>
    {children}
  </BaseLayout>
)
```

MDX内でdefault exportすることができて、childrenにはこのMDXのJSXが渡ってきます。

## MDXのシンタックスをいろいろ試してみる
```mdx:index.mdx
import { Counter } from "../components/Counter"
import AboutContent, { meta as aboutMeta } from "./about.mdx"

# サンプルです

<div>
  { ["hoge", "fuga"].map(word => (
    <p>{word}</p>
  )) }
  # これはh1にならない
</div>

<!-- コメントアウト -->

<div>
  {/* コメントアウト */}
</div>

(qiitaのmarkdownとバッティングするのでいったん回避)
\```tsx
// コードブロックが書ける
type Props = { name: string }
export const Component: FC<Props> = ({ name }) => <>{name}</>
\```

- exportのない変数宣言はできない

export const hoge = "hoge"

- 文章とexportの間に空行がない場合は文章として扱われる
- constであればちゃんとMDX内でも再定義できない
export const hoge = "hoge"

<!--
export あああ
これはexport構文と解釈されてエラーとなる
-->

もちろんコンポーネントも使える
<Counter />

別のMDXをimportすることもできる(jsxなら当たり前ではある)
<AboutContent />
<div>
  title: { aboutMeta.title }
</div>

定義済みの変数があったりする
import MDXContent from "./about.mdx" // MDXContentはjsxで宣言される関数名ぽい
```

```mdx:about.mdx
export const meta = {
  title: "about"
}

# aboutページです
```

vercelに↑のmdxをデプロイしたので、興味がある方は実際にどう表示されているか確認してみてください。
https://ikea-dless-mdx-sample.vercel.app/

## 所感
Next.js + MDXはドキュメントやブログでは結構使えるんじゃないかなと思っています。実際はfsでゴニョゴニョする部分があったり、検索機能を入れようと思うと一筋縄ではいかなさそうだったりすると思いますが...
gatsbyjsとの比較はしていないので、どちらが良いかはわかりませんが、vercelも込みで考えるとこの辺のエコシステムはNext.js強いよなーと思います。

## 余談
https://storybook.js.org/docs/react/writing-docs/mdx
以前、試験的に導入したのですが、storybookもMDXに対応していて、これが結構コンポーネントのドキュメントを書くのには向いている気がしています。仕様と実際のコンポーネントが同時に確認できて、非常に良いです。
加えて、MDXでもstoryshotsでドキュメント化したコンポーネントは漏れなくsnapshotテスト対象にできたりします。

<img width="600px" alt="button-story-default-docs-code.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42198/fdf9f2b3-b1de-3b6e-c187-5740a118bc5b.png"></img>


## 参考
- https://mdxjs.com/
- https://nextjs.org/docs/getting-started
- https://www.gatsbyjs.com/docs/mdx/