📔
Svelte + Vercel でブログサイトを構築してみた
2024-12-26 00:00
タイトルの通りですが、SvelteKit + Vercel を使用して本ブログサイトを構築しました。
以下についてご紹介します。
- 使用技術
- 良かったこと
- 苦戦したこと
使用技術
以下の構成です。
- Svelte 5 + TypeScript
- Tailwind CSS
- MDsveX
npx sv create
を使用して環境構築したため、Svelte は最新バージョンを使用。
これら環境構築の流れについては Zenn スクラップに記録しています。

TypeScript の採用については、癖です。 型安全のため、JS よりも TS をとりあえず採用するという思考になっています。
Tailwind CSS は、環境構築の流れで簡単に導入できたから採用した、が理由になります。 が、それと同等の理由として、自分がデザイン周りを得意としていないため、ある程度決まった形でスタイルを当てれる Tailwind CSS は開発がしやすいです。 そのため、採用しました。
MDsveX とは、Svelte 用の Markdownプリプロセッサです。 要は、Markdownファイルを良い感じに扱う Svelte 用のライブラリです。 Svelte + Markdown といえば MDsveX がド定番のようで、深く考えず採用です。
良かったこと
- 環境構築が簡単。
- Svelte + Vercel のデプロイが簡単。
- Svelte 自体の学習コストがさほど高くない。
- とにかくお金がかからない。
環境構築が簡単
本当に簡単です。
npx sv create
を実行するだけです。
実行したあとは、自分が必要としているライブラリをチェックして同時にインストールするだけです。
チェックをしてインストールすれば、必要な設定がされている状態で環境構築が完了します。
ここまで簡単に環境構築が出来るのは本当にありがたいです。
Svelte + Vercel のデプロイが簡単
こちらも、ビックリするぐらい簡単です。
環境構築後、とりあえず GitHub リポジトリを作成して、 first commit
します。
その後、 Vercel にアクセス、GitHubアカウントでアカウント登録を進めていきます。
上記の流れについては、以下のQiita記事が大変参考になりましたので、これ見れば楽勝です。

一度環境が出来てしまえば、あとは main
ブランチにプッシュするたびに自動的にデプロイが走ってくれます。
しかも Vercel がすごいのは、デプロイの簡単さだけでなく、機能面でも充実しています。
- アクセスログの確認が可能
- Speed Insights でSEO的に優れているかの確認が可能
- 監視も可能
- その他も…
Speed Insights
Observability
Analytics
Vercel すごい…
Svelte 自体の学習コストがさほど高くない。
Svelte ファイルは以下のような構成となっています。
<script>
// js(ts) 処理を記載
</script>
<!-- HTML を記載 -->
<style>
/* CSS を記載 */
</style>
Vue.js の単一ファイルコンポーネント(SFC)に近い構成だなーというのが感想です。
一つのファイルで基本的に完結するため、まとまりもあって管理しやすいです。
また、固有の記法という部分も React や Vue に比べると少ないため、生のHTML, CSS, JS が分かっていればスムーズに学習を進めることが可能だと思います。
とにかくお金がかからない。
本当にお金がかかりません。
固有ドメインを使用したいため、お名前.com でドメインを取得した 1000円程度です。
Vercel は hobby プランという商用利用禁止の最低スペックプラン(hobbyプラン)であれば無料で利用できます。
また、フロントエンドのみのアプリケーションということもあり、DB等も持つ必要はないため、イニシャルコストもランニングコストも基本的に掛かりません。
お金が掛かるだけで開発/運用することに躊躇いが生まれますが、基本0円なので気軽に始めることが出来るのは大きなメリットでした。
苦戦したこと
- Vercel デプロイが可能な Node バージョンについて。
- Vercel 環境下で Markdown ファイルのリアルタイム読み込み。
- Tailwind CSS を使用した style の実装について。
Vercel デプロイが可能な Node バージョンについて
執筆時点でのNode の最新バージョンは、 v23.3.0
です。
とりあえず最新が良いだろうという気持ちで進めていましたが、ビルド時にエラーが発生しました。
error during build:
Error: Unsupported Node.js version: v23.3.0. Please use Node 18 or Node 20 to build your project, or explicitly specify a runtime in your adapter configuration.
at get_default_runtime (file:///vercel/path0/node_modules/@sveltejs/adapter-vercel/index.js:15:8)
at Object.adapt (file:///vercel/path0/node_modules/@sveltejs/adapter-vercel/index.js:155:67)
at Object.adapt (file:///vercel/path0/node_modules/@sveltejs/adapter-auto/index.js:106:19)
at adapt (file:///vercel/path0/node_modules/@sveltejs/adapter-auto/index.js:117:31)
at async adapt (file:///vercel/path0/node_modules/@sveltejs/kit/src/core/adapt/index.js:38:2)
at async finalise (file:///vercel/path0/node_modules/@sveltejs/kit/src/exports/vite/index.js:954:7)
at async Object.handler (file:///vercel/path0/node_modules/@sveltejs/kit/src/exports/vite/index.js:984:5)
at async PluginDriver.hookParallel (file:///vercel/path0/node_modules/rollup/dist/es/shared/node-entry.js:20796:17)
at async Object.close (file:///vercel/path0/node_modules/rollup/dist/es/shared/node-entry.js:21774:13)
at async build (file:///vercel/path0/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:65466:17)
Error: Command "npm run build" exited with 1
開発時点でサポートされているNode.js は、 v18 or v20 でした。
そのため、 svelte.config.js
の config.kit.adapter
を以下のように修正しました。
import adapter from "@sveltejs/adapter-vercel";
...
kit: {
adapter: adapter({
runtime: "nodejs20.x",
}),
}
これで、node v20 でビルドが走るようになり、解決です。
Vercel 環境下で Markdown ファイルのリアルタイム読み込み。
プロジェクトルート直下の /articles
で管理している .md
ファイルを、実行時に fs
ライブラリを使用してファイル読み込みし、そこから MDsveX を通して HTML に変換し、描画するという方法を取っていました。
// 一例
import fs from "fs";
import path from "path";
const ARTICLE_DIR = path.resolve("articles");
const articleFiles = fs.readdirSync(ARTICLE_DIR);
このやり方で、ローカル環境では問題なく機能していました。
しかし、これらを Vercel にデプロイしてみると、Markdownで作成した記事が1件も取得することが出来ませんでした。
Vercel > Source を確認してみると、ビルド後のファイルに Markdown のファイルが含まれないことが判明し、それが原因であると突き止めました。
Vercel デプロイ時に npm run build
を実行するのですが、プロジェクトルート直下の articles
ディレクトリは対象にならないようです。
何か工夫をすればビルド対象にできたのかもしれないのですが、シュッと解決することが難しかったので、代案を考えました。
それは、 /articles
の Markdownファイルを JSONファイルに変換し、ビルド対象である src 配下に出力する という方法です。
色々すっ飛ばしているような気がするので、この解決法に至った理由を記載します。
- 記事情報はアプリを構成する
src
情報として(何となく)扱いたくなかった。 - 読み込み時に都度Markdownファイルを読み込んで整形して…という一連の処理が煩わしかった。
src
情報として(何となく)扱いたくなかった。
記事情報はアプリを構成する “記事情報はアプリとは切り離された情報” というのが僕の考えです。
実装都合で一緒にしても問題はないのですが、変なこだわりといいますが、何なんでしょうね。
でもこういう直感は大事にするように心掛けていたため、この感覚を貫くことにしました。
うまく表現出来ないのですが、“記事情報は外部に保存されている情報” のような認識です。
投稿者の都合で増減はしますし、加筆修正も可能です。
いわばデータベースに保存している情報のような感じでしょうか。
これをアプリケーションを動かす src
で管理してそれなりの頻度で変更が生じるということに違和感を覚えたため、別で扱いました。
読み込み時に都度Markdownファイルを読み込んで整形して…という一連の処理が煩わしかった。
fs
ライブラリを使用して都度実行時に読み込んで、メタデータの部分を解析してオブジェクトに変換して、コンテンツ部分はMDsveXを使ってHTML形式に変換して… というのをページを開く毎に都度実行していました。
そもそも記事情報は一度書いてしまえば頻繁に変更が入るような情報ではなく、毎回の描画のためにゴチャゴチャ実行することに無駄を感じてました。
今回の問題をキッカケに、「ビルド時のみファイルの読み込みを実行し、その結果をデータとして持たせておけば無駄が少ないのでは!?」という考えに至りました。
ビルド時であればプロジェクトルート直下に配置した articles
を fs
で参照可能です。
package.json の scripts.build
を以下のように修正しました。
{
...,
"scripts": {
...,
"build": "npm run build:article && vite build",
...,
"build:article": "node scripts/build-articles.js",
}
}
この "build:article"
は、記事情報である Markdownファイルをすべて読み込み、それを src/.build/articles.json
に出力するスクリプトです。
出力先の src/.build
は gitignore で監視対象外としています。
また、 src
配下であればビルド対象となり、以下のように参照可能です。
import allArticles from "$build/articles.json";
$build
は、 svelte.config.js
で設定した alias です。
// svelte.config.js
const config = {
kit: {
alias: {
$build: "./src/.build",
},
},
};
(未検証だが)他に考えうる解決法
/articles
をsrc
ディレクトリ配下である/src/articles
に配置していれば、そもそもビルド対象となっていたのかもしれません。- 画像と同様に、
static
ディレクトリ配下に配置していればほぼ確実にビルド対象になっていたでしょう。- ただこの方法は、生Markdownファイルが容易に参照できるようになるということなので、何かあまり良い気はしない方法。
Tailwind CSS を使用した style の実装について
一瞬だけ躓いた、というものです。
svelte ファイルで style を当てることが可能なのですが、そのときに生CSSではなく、Tailwind CSS を使用して当てたいと考えました。
理由は、Tailwind CSSのクラスを使用した統一的なスタイルで管理したかったためです。
要所要所で生CSS を使った微妙なスタイルの当て方( 例: padding: 7px
)をしてしまうと、変なズレが生じてしまったり、管理が面倒になりそうという感想を持ちました。
そのため、以下のようにstyle を当てました。
<style>
.passive-page-link {
@apply cursor-not-allowed rounded bg-gray-100 px-4 py-2 text-gray-400;
}
</style>
この @apply
は、Tailwind CSS のクラスを使ってstyleを当てる方法です。
しかし、これだと反映されませんでした。
HTML上で指定したクラス名だと適用されているため、Tailwind CSS の設定がおかしいということでは無さそうです。
その原因ですが、 Style が postcss になっていなかった ことです。
style lang
を設定してないため、生CSSとして認識されていました。
正しくは以下です。
<style lang="postcss">
.passive-page-link {
@apply cursor-not-allowed rounded bg-gray-100 px-4 py-2 text-gray-400;
}
</style>
地味にハマったので、紹介です。
終わりに
今回始めてSvelte, Vercel に触れましたが、サクッとWebサイトを立ち上げたいときに相性がかなり良いことを実感しました。
Next.js, Nuxt.js 等は王道ではありますが、個人レベルでの立ち上げやPoCレベルの場合は機能が必要以上の場合が多いです。
そういうときに Svelte を使用し、Vercel にデプロイするのはかなり効率的だと思います。
Svelte について深く学習する予定はないですが、細く長くお世話になろうと思います🙏