mmag

ハマったことメモなど

ブログにテーマを設定できるようにした

最近つくってるブログの話。設定を変える機能はまだつくっていなくて固定値がAPIから返ってくるだけなので「設定できるようにした」というのはタイトル詐欺で、実際は「APIレスポンスに応じてテーマが変わるようにした」が正しい。

仕組み

SSRするときにAPIから取ってきたJSONの中の値を見て、Dynamic Importしてる。一部割愛したコードはこんな感じ。

// src/pages/p/[id].tsx

import blogLayout from '@/components/BlogLayout'
import blogName from '@/components/BlogName'
import postList from '@/components/PostList'
import postEntry from '@/components/PostEntry'

export async function getServerSideProps(context: ServerSideContext) {
  const { params, req } = context
  const api = new APIClient()

  const { json: { blog } } = await api.getBlog(host)

  return { props: { blog } }
}

export default function PostPage(props: Props) {
  const theme = props.blog.theme

  // `theme`の値によって返ってくるcomponentが変わる
  const BlogLayout = blogLayout(theme)
  const BlogName = blogName(theme)
  const PostList = postList(theme)
  const PostEntry = postEntry(theme)

  return (
    <>
      <Head>
        ...
      </Head>

      <BlogLayout>
        <BlogName name={props.blog.name} />
        <PostList>
          <PostEntry post={props.post} />
        </PostList>
      </BlogLayout>
    </>
  )
}
// src/components/BlogLayout/index.tsx

import dynamic from 'next/dynamic'

export interface Props {
  children: React.ReactNode
}

export default (name: string) => {
  return dynamic<Props>(() => import(`./${name}.tsx`))
}

当初は記事とかに当てるスタイルのいろんな値をCustom Propertyにしておいて、<Head>に入れるCSSを変えることでCustom Propertyの値を変えて、結果テーマが変わるみたいなことを考えていたけど、すぐにこれ無理だなってことに気づいてやめました。2つ目のテーマを書こうとした時点で3テーマ目をメンテできる気がしなかったし、マークアップが変えられないと色や余白を変える程度の自由度しかなく、苦労の割に得られるものがショボすぎたので。今回の実装の場合、テーマ間での制約は

<BlogLayout>
  <BlogName name={props.blog.name} />
  <PostList>
    <PostEntry post={props.post} />
  </PostList>
</BlogLayout>

という構造になっているってことだけで、その中では何をやってもいいし、要らなくなったときに消しやすくできたかなと思います。あと、これはNext.jsさんありがとうございますって感じですが、Dynamic Importのおかげで必要なchunkしか取得しないので心置きなくテーマを増やせます。