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
No description available.

SvelteKitとは
SvelteKitは、Svelteをベースにしたフルスタックフレームワークで、Next.jsに似た機能を提供します。例えば、以下のような特徴があります:
- ファイルベースのルーティング:フォルダ構造でページやAPIを定義。
- SSR/SSG対応:サーバーサイドレンダリングや静的サイト生成が簡単。
- APIルート:バックエンドAPIを同じプロジェクト内で構築可能。
SvelteKit アプリはデフォルトではサーバーでレンダリングを行うため、優れた初期ロードパフォーマンスと SEO 特性を備えています。
初回のロードのあとは (モダンなシングルページアプリ、SPA のような) クライアントサイドナビゲーションに移行するため、ユーザーが画面遷移する際の全読み込みを回避できます。
イントロダクション • Docs • Svelte
No description available.

簡単なブログ風アプリを作ってみる
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-query
2.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 を試してみたい方や、他フレームワークとの比較検討をしている方の参考になれば嬉しいです。