ヘッドレスCMSのSanityを使ってみた( Next.js Page Router編 )

WordCamp Asiaで話題の「Composable Content」について紹介。複数のページや媒体でコンテンツを合成するアプローチで、LPやコンテンツマーケティングが効率的に。Sanity.ioを使って記事作成方法やプロジェクト管理を体験。Sanityの特徴は、SaaS側がAPIや管理機能を提供し、ユーザーが管理UIを自身のアプリに組み込む点。コードで記事やコンテンツを管理し、参照関係を指定できるところが特に便利。CSSの設定やフローは親切で、セキュリティ面も強固。SANITYを使う際は、CQRS的なコンテンツ配信フローも検討可能。

広告ここから
広告ここまで

目次

    WordCamp Asiaで耳にすることが増えてきている「Composable Content」がずっと気になっています。記事単位ではなく、もっと細かい単位でコンテンツを管理し、複数のページや媒体でコンテンツを合成していく考え方のことです。この概念が採用できると、ターゲットユーザーごとにLPを複数作成したり、コンテンツマーケティングなどを目指す際に、複数の場所に個別に情報を書く必要がなくなります。それによって例えば商品の仕様が変わったり、店舗の営業時間や場所が変わった場合に、置換が必要な記事対を検索したりコンテンツの入れ替え作業を行う必要がなくなってくれます。

    今回はSanity.ioを初めて触るので、アカウント登録と記事作成の方法などを簡単に触ってみます。

    Sanityアカウントを作成する

    CMSのSaaSですので、アカウントを作成しましょう。https://sanity.io で”Get Started”から新規アカウントが作れます。

    シングルサインオンが使えますので、ログイン情報を個別に記憶する・させる必要がないのも、気軽に試せてよいですね。

    アカウントを作成すると、プロジェクトや組織情報などを管理するダッシュボードに移動します。Sanityでは管理するコンテンツを「プロジェクト」としてまとめることができる様子です。Composableな使い方をするため、サイト単位でプロジェクトを作るよりは、事業部やビジネスとしてのプロジェクト単位でSanityのプロジェクトを管理するのがよさそうです。

    チュートリアルをやってみる(2023年ごろ版)

    試したメモが、2023年ごろのものですので、今はすこし変わっているかもしれません。ご注意ください。

    ドキュメントにチュートリアルが用意されています。ここからはじめることで、「Sanityとは?」や「Composable Contentってなに?」みたいなところを体験しながら知ることができそうです。

    https://www.sanity.io/docs/overview-introductionから、ビデオやデモプロジェクトが選べます。今回はテンプレートからサイトをつくってみました。

    今回は個人ブログでのユースケースを試すため、[Personal website with built-in content editing]テンプレートをから始めます。

    1クリックでテンプレートをVercelにデプロイできます。ローカルにcloneしても良いのですが、せっかくなのでVercelにデプロイしてみましょう。

    デプロイはGitHubリポジトリをforkした後、自分のGitHubアカウントからVercelへデプロイする流れでした。orgアカウントは有料ですし、個人的に試すにはハードルが少し上がりますので、まぁそうだろうなという印象ですね。

    Sanityアカウントへの接続もこのフロー内で行います。先ほど作成したアカウントでログインしましょう。

    連携先を新規プロジェクトか既存プロジェクトどちらにするかを選べます。

    ここまで進めると、デプロイが始まりました。

    あとはNext.jsをVercelにデプロイした時と同じような流れですね。

    Sanity側を見ると、CORSの設定やトークンなどができていました。もしかすると、元々あるやつかもしれませんが、ローカル開発時に CORSでハマりにくそうなので、親切設計ですね。

    Localローカルでも動かしてみる

    続いてローカルでも動かしてみます。Vercelへデプロイした際にForkしたリポジトリが増えています。Vercelの管理画面からリポジトリへ飛べるので、そこから向かいましょう。

    通常のNext.jsプロジェクトですので、git cloneするだけです。

    % git clone [email protected]:hideokamoto/practice-personal-website-nextjs-sanity.git
    % cd practice-personal-website-nextjs-sanity

    Vercel上のサイトとリンクしておきましょう。

    % npx vercel link
    
    Vercel CLI 28.17.0
    > > No existing credentials found. Please log in:
    ? Log in to Vercel github
    > Please visit the following URL in your web browser:
    > Success! GitHub authentication complete for [email protected]
    ? Set up “~/sandbox/vercel”? [Y/n] 
    ? Which scope should contain your project? hideokamoto
    ? Link to existing project? [y/N] y
    ? What’s the name of your existing project? practice-personal-website-nextjs-sa
    nity
    ✅  Linked to hideokamoto/practice-personal-website-nextjs-sanity (created .vercel and added it to .gitignore)

    リンクすると、環境変数をpullできます。手で設定しても問題ないのですが、こっちの方が手早くておすすめです。

    % npx vercel env pull
    
    > Downloading `development` Environment Variables for Project practice-personal-website-nextjs-sanity
    ✅  Created .env file [431ms]

    あとはNext.jsのサイトをローカルで立ち上げる手順を進めるだけです。

    % npm i
    % npm run dev

    立ち上がりました。

    Sanity Studioでコンテンツを管理する

    Sanityの面白い点は、「CMS機能をNext.jsなどのアプリケーション側に積むこと」です。ヘッドレスCMSは「そのSaaSベンダが提供するドメイン・UIで記事を管理する。APIからも操作できます」というパターンがよく目につきます。しかしSanityの場合は、SaaS側はAPIや管理機能だけを提供し、コンテンツを管理するUIはユーザーがアプリに組み込む形となります。立ち上げたサイトに手順がありますので、従っていきましょう。

    Sanity Studioへ入るには、Sanityアカウントでのログインが必要です。

    ログインすると、Next.jsアプリ内にある管理UIが開きます。Sanity StudioはSanityのSDKを利用して、コンテンツ管理UIを自前のアプリケーション上でホストするための仕組みです。

    Next.js側のコードも見てみましょう。pages/studio/[[...index]].tsxを見ると、Sanity Studioを動かすための実装が書かれています。

    import Head from 'next/head'
    import { NextStudio } from 'next-sanity/studio'
    import { NextStudioHead } from 'next-sanity/studio/head'
    import { StudioLayout, StudioProvider } from 'sanity'
    import config from 'sanity.config'
    import { createGlobalStyle } from 'styled-components'
    
    const GlobalStyle = createGlobalStyle(({ theme }) => ({
      html: { backgroundColor: theme.sanity.color.base.bg },
    }))
    
    export default function StudioPage() {
      return (
        <>
          <Head>
            <NextStudioHead favicons={false} />
          </Head>
    
          <NextStudio config={config}>
            <StudioProvider config={config}>
              <GlobalStyle />
              <StudioLayout />
            </StudioProvider>
          </NextStudio>
        </>
      )
    }
    

    このように管理画面を自身でホストできるため、アクセス権限の管理やUIやコンテンツ管理に関するUXのカスタマイズなどがやりやすくなります。セキュリティ的な要件が厳しい、大企業などでは使いやすそうですね。

    記事管理はSanity Studioから

    Sanity Studioの中にコンテンツを行う機能が用意されています。デフォルトのコンテンツタイプが用意されていますので、とりあえず記事を書いてみましょう。

    簡単なビジュアルエディタも搭載されている様子です。ただしWordPressのように、1つのフィールドにリッチなコンテンツを書くのではなく、「パーツだけを用意し、アプリケーション側で必要に応じて組み立てる」フローをとるため、あまりリッチな体験は期待するべきではなさそうです。

    プレビュー機能もSanity Studioが提供してくれます。プレビュー系の組み込みがSDKに任せれるのは良いですね。

    コンテンツの取得は、Sanity独自(?)のクエリであるgroqを使います。GraphQLに似た感じではありますので、ヘッドレス系のサイト構築経験がある方はそこまで迷わないかなと思いました。

    import { groq } from 'next-sanity'
    
    export const homePageQuery = groq`
      *[_type == "home"][0]{
        _id,
        footer,
        overview,
        showcaseProjects[]->{
          _type,
          coverImage,
          overview,
          "slug": slug.current,
          tags,
          title,
        },
        title,
      }
    `

    コンテンツの登録は、コードで行います。ここで定義した内容に基づいてSanity Studioで記事管理が行えます。

    import { HomeIcon } from '@sanity/icons'
    import { defineArrayMember, defineField, defineType } from 'sanity'
    
    export default defineType({
      name: 'home',
      title: 'Home',
      type: 'document',
      icon: HomeIcon,
      // Uncomment below to have edits publish automatically as you type
      // liveEdit: true,
      fields: [
        defineField({
          name: 'title',
          description: 'This field is the title of your personal website.',
          title: 'Title',
          type: 'string',
          validation: (rule) => rule.required(),
        }),
    ...

    バリデーションなども定義できるのは、ユニークですね。

    コンテンツのリレーションを作る

    Projectというコンテンツタイプが登録されていますので、こちらにもコンテンツを追加してみましょう。

    ユニークなのが、referenceがつけれることです。projectコンテンツタイプに保存された別のコンテンツとの関連性をつけれる様子です。

    定義をつける際に、referenceを設定すると他のコンテンツタイプも指定できます。

        defineField({
          name: 'showcaseProjects',
          title: 'Showcase projects',
          description:
            'These are the projects that will appear first on your landing page.',
          type: 'array',
          of: [
            defineArrayMember({
              type: 'reference',
              to: [{ type: 'project' }],
            }),
          ],
        }),

    ここまでの変更をVercelにデプロイし直してみましょう。

    リファレンスを設定したコンテンツなども、取得・表示できていました。

    触ってみての感想

    groqやSanity Studioの組み込みなど、 1から組み込みを始めるにはちょっとハードルが高い部分はあるかもしれません。ただしポートフォリオを一度登録しておけば、それを紹介するブログやLPを作る場合にも、リファレンス機能で取得するだけにできる点など、「同じ内容・意図のコンテンツを2回以上書かないようにする」ための仕組みがものすごく作り込まれている印象があります。

    コンテンツのハブにも使えると謳っているCMSですので、CQRS的なコンテンツ配信フローを検討されている場合は、SanityをRead用データベースとして検討しても良いかもしれません。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark