Nuxt Content v3 + Nuxt Hub を使って爆速で個人ブログを作って公開する

  • Nuxt.js

  • Vue.js

  • Cloudflare

  • Tailwind CSS

2025 年 1 月にリリースされた Nuxt Content v3 とNuxt Hubを使って個人ブログを爆速で作っていきます。

ブログはソフトウェア開発者のセルフマーケティングで使える最良のメディアのひとつだ。実際、自分のキャリアを大事に考えるすべてのソフトウェア開発者は、ブログの作成に投資すべきだと私は強く思っている。

ジョン・ソンメズ. SOFT SKILLS ソフトウェア開発者の人生マニュアル 第2版 (p.186).

はじめに

はじめまして、フリーランスのエンジニアとして働いているつきやまです。
Vue, Nuxt, CSS が好きです。
技術以外だと最近はポケポケと短歌を嗜んでいます。

この度、個人ブログを作る運びとなり、本記事は記念すべき一本目の記事です。
せっかくなので一本目の記事は本ブログのような個人ブログを爆速で作る方法の記事を書こうと思います。

記事のテーマとして個人ブログを爆速で作ると掲げているので、ブログの根幹となる機能の解説しかしません。
(=細かな機能やUIについては解説しないです)

Nuxt Content は 2025 年 1 月に v3 がリリースされました。
v3 の主な機能は公式ブログにまとめられています。

Announcing Nuxt Content version 3

https://content.nuxt.com/blog/v3

事前準備

さっそく実装に入りたいところですが、実装に入る前に各種アカウント作成をする必要があるので作成がまだの方は作成をお願いします。
(すでにアカウントお持ちの方は飛ばしてください。)

本記事では Nuxt Hub を利用してデプロイを行います。
Nuxt Hub を利用する際に必要なアカウント類の登録を行います。

GitHub

GitHub アカウントと公開するサービスのリポジトリを用意をお願いします。

Build software better, together

https://github.com/signup

Cloudflare

Cloudflare Pages にホスティングするのでアカウントの作成をお願いします。

Just a moment...

https://dash.cloudflare.com/sign-up

Nuxt Hub

Nuxt Hub を用いて Cloudflare にデプロイするのでアカウント作成をお願いします。

NuxtHub Admin: Manage full-stack Nuxt apps, on the edge.

https://admin.hub.nuxt.com/?utm_source=hub-docs&utm_medium=header&utm_campaign=signup

環境構築

Nuxt インストール

Installation · Get Started with Nuxt v3

https://nuxt.com/docs/getting-started/installation

$ npm create nuxt sample-blog

プロジェクト名は任意の名前をつけてください。
インストールが済んだらディレクトリを移動して、開発サーバーを起動してみましょう。

cd sample-blog
npm run dev

localhost:3000にアクセスして Welcome ページが表示されていれば完了です。

デプロイの際にGitHubリポジトリと紐づける必要があるのでリポジトリの作成・紐付けも行います。

Nuxt Content インストール

Installation

https://content.nuxt.com/docs/getting-started/installation

npx nuxi module add content

Nuxt Hub インストール

Installation · NuxtHub

https://hub.nuxt.com/docs/getting-started/installation

npx nuxi module add hub

Tailwind CSS (Nice to Have)

(Tailwind CSS はインストールしなくても問題ないです。)

Installing with Vite - Installation

https://tailwindcss.com/docs/installation/using-vite

npm install tailwindcss @tailwindcss/vite

~/assets/css/tailwind.cssを新規作成します。

tailwind.css
@import "tailwindcss";

nuxt.config.tsに Tailwind CSS の記述を追加します。

nuxt.config.ts
import tailwindcss from '@tailwindcss/vite'

export default defineNuxtConfig({
  // ...
  css: ['~/assets/css/tailwind.css'],
  vite: {
    plugins: [tailwindcss()],
  },
})

デプロイしてみる

一通りの環境構築を終えたのでこの段階で一旦デプロイしてみましょう。

デプロイコマンド

npx nuxthub deploy

デプロイコマンドを実行するといくつか質問されます
基本的にデフォルトの回答で大丈夫だと思います。

region だけ Asia Pacific を選びました。

$ npx nuxthub deploy
NuxtHub CLI
 No project is linked with the NUXT_HUB_PROJECT_KEY environment variable.
  Deploy ~/projects/sample-blog to NuxtHub?
  Yes
  Select a project
  Create a new project
  Project name
  sample-blog
  Select a region for the storage
  Asia Pacific
  Production branch (git)
  main
 Project sample-blog created
 Connected to tsukiyama-3 team.
 Linked to sample-blog project.

問題なければビルドが走ります。
ビルドに成功するとデプロイ先の URL が表示されるのでアクセスしてみましょう。

Welcome ページが表示されていたら完了です。

実装

環境構築も終えたのでいよいよ実装に入っていきます。

コレクション定義

v3 では従来のファイルベースの管理から SQL のデータベースシステムに移行しました。
ファイル管理の方法が変わったからといって内部でいい感じに .sqlite ファイルを生成してくれているので使う際に特に意識する必要はないと思います。

Markdown

https://content.nuxt.com/docs/files/markdown

Nuxt Content v3 ではコレクションを定義してコンテンツを管理します。
コレクションとは、関連するコンテンツのグループです。

コレクションの定義はcontent.config.tsで行います。
content.config.tsを新規作成します。

コレクションには任意でスキーマの定義もできます。

content.config.ts
import { defineContentConfig, defineCollection, z } from '@nuxt/content'

export default defineContentConfig({
  collections: {
    blog: defineCollection({
      type: "page",
      source: "blog/*.md",
      // スキーマ定義
      schema: z.object({
        title: z.string(),
        description: z.string(),
        image: z.string(),
        published: z.boolean(),
      })
    }),
  },
})

コンテンツ作成

コンテンツはルート直下に/contentを作成して管理します。
ためしに~/content/blog/配下にいくつか以下のようにマークダウンファイルを作成してみます。

content/blog/0401.md
---
title: 4月1日の記事
description: 4月1日の記事です。
image: https://picsum.photos/584/328
published: true
---

# タイトル

パラグラフ

TOP ページ

~/pages/indexを作成します。

pages/index.vue
<script setup lang="ts">
const { data } = await useAsyncData('blog', () =>
  queryCollection('blog').all(),
)
</script>

<template>
  <div>
    <ul class="space-y-8">
      <li
        v-for="article in data"
        :key="article.path"
        class="list-none divide-y divide-gray-300 hover:opacity-70"
      >
        <NuxtLink
          :to="article.path"
          class="gap-x-4"
        >
          <div class="space-y-1 text-blue-600 underline">
            <h3 class="text-base md:text-xl font-bold">{{ article.title }}</h3>
            <p class="text-sm md:text-base opacity-80">
              {{ article.description }}
            </p>
          </div>
        </NuxtLink>
      </li>
    </ul>
  </div>
</template>

コンテンツの取得はqueryCollectionを用います。
publishedtrueの記事を取得するように絞り込んでいます。

queryCollection

https://content.nuxt.com/docs/utils/query-collection

app.vueも修正します。

app.vue
<template>
  <div>
    <NuxtPage />
  </div>
</template>

画面確認すると/content配下に配置した記事が表示されていると思います。

記事ページ

記事ページとしてpages/blog/[...slug]/index.vueを作成します。

<!-- pages/blog/[...slug]/index.vue -->
<script setup lang="ts">
const route = useRoute()
const { data } = await useAsyncData(route.path, () =>
  queryCollection('blog').path(route.path).first(),
)
</script>

<template>
  <div class="max-w-[800px] mx-auto">
    <article
      v-if="data"
      id="article"
      class="space-y-12"
    >
      <div class="space-y-4">
        <img
          ref="image"
          :src="data.image"
          alt=""
          width="584"
          height="328"
          class="mx-auto"
        >
        <h1 class="font-bold text-xl md:text-3xl">
          {{ data.title }}
        </h1>
        <p class="opacity-80 text-sm md:text-base">
          {{ data.description }}
        </p>
      </div>
      <ContentRenderer
        :value="data"
        class="space-y-8"
      />
    </article>
    <div v-else>
      <h1>記事が見つかりませんでした</h1>
    </div>
  </div>
</template>

画面を確認してみるとマークダウンで書いた記事が表示されていると思います。

公開する

Nuxt Hub のダッシューボードから Git リポジトリを紐づけることによって、mainブランチにpushするだけでデプロイできるようになります。簡単ですね。
mainではないブランチにpushするとプレビュー環境が作られます。嬉しいですね。

ダッシュボード > 該当プロジェクト > Settings > General > Git repository > Link repository

(Nuxt Content を Cloudflare Pages にホスティングする場合は、D1データベースに紐付ける必要があるのですが、その辺は Nuxt Hub がいい感じにしてくれているっぽいです。)

おわりに

本記事ではNuxt Contentを使って爆速で個人ブログを作ることをテーマになるべく寄り道をせずに最低限のステップだけを紹介しました。

細かい機能やUIについてはご自身の好みでカスタマイズしていってください。

本ブログのコードは公開しています。
本記事で実装しているコードとは一部異なりますが気になる箇所があればご覧ください。
(誤字脱字や内容の誤りなどがありましたらコメントやIssueを建てていただけるとありがたいです。)

GitHub - tsukiyama-3/tsukiyama-blog

https://github.com/tsukiyama-3/tsukiyama-blog

今後も主にフロントエンドにまつわる記事を書いていく予定ですので、お見知り置きを。