SvelteKit を使って分かった、React や Next.js との違い
ReactやNext.jsを使う中で、ふと「もっと軽量で、開発体験がシンプルなフレームワークはないだろうか?」と思うことが増えてきました。そんな時に目に留まったのがSvelteKitです。
SvelteKitは「必要な機能は最小限の抽象で、HTMLやブラウザの標準挙動を尊重しながら提供する」という設計思想を持っています。
そこで、今回は簡単なブログアプリの作成を通して、実際に触ってみて分かったReact/Next.jsとの設計や開発体験の違いを具体的なコード例とともに紹介していこうと思います。
SvelteとSvelteKitについて
Svelteの特徴
Svelte(スベルト)は、コンパイラベースのJavaScriptフレームワークです。
ReactやVueのような仮想DOMを使わず、ビルド時に純粋なJavaScriptに変換されます。これにより、ランタイムのオーバーヘッドが減り、パフォーマンスが向上します。
例えば、Reactでカウンターを作る場合、以下のように useState が必要です。
// React
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}一方、Svelteでは以下のようにシンプルに書けます。
<!-- Svelte -->
<script>
let count = 0;
</script>
<button on:click={() => count++}>
Count: {count}
</button>countが変更されると、Svelteが自動でUIを更新します。この直感的な記述が、Svelteの開発体験(DX)の魅力です。
Svelte • Web development for the rest of us
Web development for the rest of us
svelte.jp
SvelteKitとは
SvelteKitは、Svelteをベースにしたフルスタックフレームワークで、Next.jsに似た機能を提供します。例えば、以下のような特徴があります:
- ファイルベースのルーティング:フォルダ構造でページやAPIを定義。
- SSR/SSG対応:サーバーサイドレンダリングや静的サイト生成が簡単。
- APIルート:バックエンドAPIを同じプロジェクト内で構築可能。
SvelteKit アプリはデフォルトではサーバーでレンダリングを行うため、優れた初期ロードパフォーマンスと SEO 特性を備えています。
初回のロードのあとは (モダンなシングルページアプリ、SPA のような) クライアントサイドナビゲーションに移行するため、ユーザーが画面遷移する際の全読み込みを回避できます。
イントロダクション • Docs • Svelte
イントロダクション • Svelte documentation
svelte.jp

簡単なブログ風アプリを作ってみる
SvelteKitのルーティング
SvelteKitはファイルベースのルーティングを採用しており、プロジェクトのsrc/routes/フォルダ内のファイル構造がそのままURLに対応します。
例えば:
src/routes/+page.svelte→ トップページ (/)src/routes/blog/+page.svelte→ ブログ一覧ページ (/blog)src/routes/about/+page.svelte→ Aboutページ (/about)
今回は、以下のページを作成します:
- トップページ(
/) - ブログ一覧ページ (
/blog):記事のリストを表示。 - 記事詳細ページ (
/blog/[slug]):個別の記事内容を表示。
1. SvelteKitプロジェクトのセットアップ
1.1 プロジェクトの作成
SvelteKitのプロジェクトは、以下のコマンドで簡単に作成できます。
npx sv create sample-blog実行後、以下の選択肢を選びます:
- テンプレート:SvelteKit minimal
- TypeScript:Yes
- その他インストールしたいもの:tailwindcss(ESLint/Prettierなどは任意)
- パッケージマネージャー:npm
セットアップ後、プロジェクトに移動して依存パッケージをインストールします:
cd sample-blog
npm install開発サーバーを起動して、初期状態を確認しましょう:
npm run devブラウザでhttp://localhost:5173を開くと、以下のようなSvelteKitのウェルカムページが表示されます。

1.2 基本レイアウトの作成
ブログの基本レイアウト(ナビゲーションとフッター)を作ります。SvelteKitでは、src/routes/+layout.svelteが全ページに適用されるレイアウトファイルです。
<script>
import "../app.css";
</script>
<header>
<h1>sample-blog</h1>
<a href="/" class="text-blue-500 underline">Home</a>
<a href="/blog" class="text-blue-500 underline">Blog</a>
</header>
<slot />
<footer>
<p>
Powered by
<a href="https://svelte.dev" class="text-blue-500 underline">Svelte</a>
</p>
<p>© 2025 Miyazaki</p>
</footer><slot />は、各ページのコンテンツを挿入する場所です。
1.3 トップページの作成
トップページを作成するため、src/routes/+page.svelteを編集します。
<div class="my-5 font-bold">
<h2 class="h2">Welcome to My Blog!</h2>
<p>This is a personal blog built with SvelteKit.</p>
</div>画面はこんな感じ。
.png)
2. Svelte-Queryのセットアップ
2.1 Svelte-Queryとは?
Svelte-Queryは、Svelte向けのデータフェッチングライブラリで、React Queryにインスパイアされています。APIリクエストを簡単に管理し、以下のような機能を提供します:
- 自動キャッシュ:取得したデータをキャッシュして再利用。
- ローディング/エラー状態:状態管理を簡潔に処理。
- 自動リフェッチ:データが古くなった場合に自動更新。
今回はこれを使用して、外部API(JSONPlaceholder)を利用することでブログデータを取得します。
Svelte-Queryをプロジェクトに追加します。
npm install @sveltestack/svelte-query2.2 QueryClient の作成
API などから取得したデータの自動でキャッシュや、再フェッチ・更新の管理を行うQueryClientを生成します。
src/lib/queryClient.jsを作成:
import { QueryClient } from '@sveltestack/svelte-query';
export const queryClient = new QueryClient();2.3 QueryClientProvider をレイアウトで使用
2.2で作成したQueryClientを用いて、全てのページ・コンポーネントで一貫してデータフェッチやキャッシュ管理をするために、QueryClientProvider をアプリ全体に適用します。
QueryClientProvider はQueryClientを適用するためのラッパーコンポーネントです。
src/routes/+layout.svelteを以下のように修正します。
<script>
import "../app.css";
import { QueryClientProvider } from "@sveltestack/svelte-query"; // svelte-query から QueryClientProvider をインポート
import { queryClient } from "$lib/queryClient"; // queryClient インスタンスをインポート
</script>
<header>
<h1>sample-blog</h1>
<a href="/" class="text-blue-500 underline">Home</a>
<a href="/blog" class="text-blue-500 underline">Blog</a>
</header>
<!-- アプリケーション全体を QueryClientProvider でラップ -->
<QueryClientProvider client={queryClient}>
<slot />
</QueryClientProvider>
<footer>
<p>
Powered by
<a href="https://svelte.dev" class="text-blue-500 underline">Svelte</a>
</p>
<p>© 2025 Miyazaki</p>
</footer>3. ブログ一覧ページの作成
src/routes/blog/+page.svelteを作成し、以下を記述:
<script>
import { useQuery } from "@sveltestack/svelte-query";
const query = useQuery("posts", async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
if (!response.ok) throw new Error("データの取得に失敗しました");
return response.json();
});
</script>
<div>
<h1>ブログ記事一覧</h1>
{#if $query.isLoading}
<div>
<p>読み込み中...</p>
</div>
{:else if $query.isError}
<p>
エラー: {typeof $query.error === "object" && $query.error && "message" in $query.error
? $query.error.message : "不明なエラー"}
</p>
{:else}
<div>
{#each $query.data as post}
<div class="my-5">
<h2 class="text-xl font-bold">
{post.title}
</h2>
<p>投稿ID: {post.id}</p>
<p>
{post.body.slice(0, 30)}...
</p>
<a href="/blog/{post.id}" class="text-blue-500 underline">
続きを読む →
</a>
</div>
{/each}
</div>
{/if}
</div>解説:
useQueryフックを使って、postsキーでAPIデータを取得。$query.isLoadingでローディング状態を表示。$query.isErrorでエラー状態を処理。- データが取得できたら(
$query.data)、#eachディレクティブで記事をループ表示。 - JSONPlaceholderの
idをslugとして詳細ページにリンク。
4. 記事詳細ページの作成
4.1 ルートデータの取得
SvelteKitでは、ダイナミックルートのために+page.js(または+page.server.js)でデータを準備できます。
src/routes/blog/[slug]/+page.jsを作成:
/** @param {{ params: { slug: string } }} context */
export function load({ params }) {
return {
slug: params.slug
};
}このload関数は、URLの[slug]部分をdata.slugとしてページに渡します。これにより、ページ側でslugを使って記事を検索できます。
4.2 ダイナミックルートの作成
記事詳細ページは、URLのslugに基づいて表示します。
src/routes/blog/[slug]/+page.svelteを作成:
<script>
import { useQuery } from "@sveltestack/svelte-query";
import { error } from "@sveltejs/kit";
export let data;
const query = useQuery(["post", data.slug], async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${data.slug}`
);
if (!response.ok) throw error(404, "記事が見つかりません");
return response.json();
});
</script>
<div class="my-5">
{#if $query.isLoading}
<div>
<p>読み込み中...</p>
</div>
{:else if $query.isError}
<p>
エラー: {typeof $query.error === "object" && $query.error && "message" in $query.error
? $query.error.message : String($query.error)}
</p>
{:else}
<h1 class="text-xl font-bold">{$query.data.title}</h1>
<p>投稿ID: {$query.data.id}</p>
<p>{$query.data.body}</p>
<a href="/blog" class="text-blue-500 underline"> ← 一覧に戻る </a>
{/if}
</div>解説:
export let dataで、SvelteKitが提供するslugを受け取る。postsからslugに一致する記事を検索。見つからない場合は404エラーを投げる。
5. 動作確認
改めて開発サーバーを起動します。
npm run devブラウザでhttp://localhost:5173を開くと、以下のようなブログサイト風のものができています。
▼ ブログ一覧ページ
.png)
▼ 記事詳細ページ
.png)
所感
今回、SvelteKit を使ってブログ風アプリを構築してみて、「HTML・CSS・JavaScriptの標準仕様を最大限活かしつつ、必要な部分だけをフレームワークが補ってくれる」 という感覚を強く持ちました。
React / Next.js のようにフックやコンポーネントライフサイクルを明示的に扱う必要が少なく、let で宣言した変数を書き換えるだけで UI が再描画されるのは、とても直感的です。
特に印象的だったのは以下の点です
-
リアクティビティがシンプル
React ではuseStateやuseEffectを意識して状態管理しますが、Svelte では単に変数を更新すれば自動で反映されるため、状態管理のためのボイラープレートが大幅に減りました。 -
API 連携が分かりやすい
+page.jsの load 関数でデータ取得を明確に分離でき、SSR/SSGの切り替えが直感的でした。Next.js のgetServerSidePropsやgetStaticPropsよりも構造がシンプルに感じました。 -
初期レンダリングが軽快
バンドルサイズが小さく、ランタイムオーバーヘッドも少ないため、初回表示までが非常に速いです。Next.js の App Router でも最適化は進んでいますが、素のパフォーマンスは SvelteKit に軍配が上がる場面がありました。
React / Next.js などのフレームワークとはまた違う、シンプルさと直感的な記述ができると感じました。
さいごに
今回は、SvelteKit を実際に触ってみて感じた React / Next.js との違いや特徴を紹介しました。
まだエコシステムの規模では React / Next.js に及ばない部分もありますが、そのシンプルさやパフォーマンスは魅力的で、特に小規模〜中規模のプロジェクトでは有力な選択肢になり得ると感じました。
この記事が、これから SvelteKit を試してみたい方や、他フレームワークとの比較検討をしている方の参考になれば嬉しいです。