【Svelte】SvelteKitとは?フルスタック開発の基礎から実践まで徹底解説

PUBLISHED 2026-02-04

Svelteは、コンパイラベースのアプローチを採用したモダンなフロントエンドフレームワークです。この記事では、Svelteの特徴とSvelteKitを使ったフルスタック開発について解説します。

Svelteの主な特徴

コンパイラベースのアプローチ

ReactやVueがランタイムで仮想DOMを使用するのに対し、Svelteはビルド時にコードをコンパイルして最適化されたバニラJavaScriptを生成します。

これにより以下のメリットがあります。

  • バンドルサイズが小さい
  • ランタイムオーバーヘッドがない
  • パフォーマンスが高い

シンプルなリアクティビティ

変数への代入だけでリアクティブな更新が発生します。

<script>
  let count = 0;

  function increment() {
    count += 1;  // これだけでUIが更新される
  }
</script>

<button on:click={increment}>
  クリック数: {count}
</button>

少ないボイラープレート

  • useStateuseEffectなどのフックが不要
  • .svelteファイル内にHTML、CSS、JSをまとめて記述
  • CSSはデフォルトでスコープ化される

組み込み機能が豊富

  • トランジション/アニメーション
  • ストア(状態管理)
  • スロット
  • コンテキストAPI

React/Vueとの比較

項目SvelteReactVue
仮想DOMなしありあり
バンドルサイズ小さい大きめ中程度
学習コスト低い中程度中程度
エコシステム成長中非常に大きい大きい

SvelteKitとは

SvelteKitは、Svelte公式のフルスタックフレームワークです。ReactにおけるNext.js、VueにおけるNuxtに相当します。

Svelte = UIライブラリ(コンポーネント作成)
SvelteKit = フレームワーク(ルーティング、SSR、API等を含む)

SvelteKitの主な機能

機能説明
ファイルベースルーティングsrc/routes/以下の構造がURLになる
SSR/SSG/CSRページごとにレンダリング方式を選択可能
APIエンドポイント+server.tsでバックエンドAPIを定義
データローディング+page.server.tsでサーバーサイドのデータ取得
フォームアクションプログレッシブエンハンスメント対応のフォーム処理

ファイル構造の例

src/routes/
├── +page.svelte          # / のページ
├── +layout.svelte        # 共通レイアウト
├── about/
│   └── +page.svelte      # /about のページ
├── blog/
│   ├── +page.svelte      # /blog のページ
│   ├── +page.server.ts   # サーバーサイドデータ取得
│   └── [slug]/
│       └── +page.svelte  # /blog/:slug の動的ルート
└── api/
    └── users/
        └── +server.ts    # /api/users のAPIエンドポイント

SvelteKitのコード例

ページ(+page.svelte)

<script>
  export let data;  // +page.server.tsから受け取る
</script>

<h1>{data.title}</h1>
<ul>
  {#each data.posts as post}
    <li>{post.title}</li>
  {/each}
</ul>

データ取得(+page.server.ts)

export async function load() {
  const posts = await db.getPosts();
  return { title: 'ブログ', posts };
}

APIエンドポイント(+server.ts)

import { json } from '@sveltejs/kit';

export async function GET() {
  const users = await db.getUsers();
  return json(users);
}

export async function POST({ request }) {
  const data = await request.json();
  const user = await db.createUser(data);
  return json(user, { status: 201 });
}

フォルダ構成のベストプラクティス

小規模プロジェクト(公式推奨ベース)

src/
├── lib/
│   ├── components/       # UIコンポーネント
│   │   ├── Button.svelte
│   │   └── Header.svelte
│   ├── server/           # サーバー専用コード
│   │   └── db.ts
│   └── utils.ts          # 共有ユーティリティ
├── routes/
│   ├── +layout.svelte    # 共通レイアウト
│   ├── +page.svelte      # トップページ
│   ├── about/
│   │   └── +page.svelte
│   └── api/
│       └── users/
│           └── +server.ts
├── app.html
└── app.css

中〜大規模プロジェクト

src/
├── lib/
│   ├── components/
│   │   ├── ui/           # 汎用UIコンポーネント
│   │   │   ├── Button.svelte
│   │   │   └── Input.svelte
│   │   └── features/     # 機能別コンポーネント
│   │       ├── auth/
│   │       │   └── LoginForm.svelte
│   │       └── users/
│   │           └── UserCard.svelte
│   ├── server/
│   │   ├── db/
│   │   │   ├── index.ts       # DB接続
│   │   │   └── schema.ts      # Drizzle/Prismaスキーマ
│   │   └── services/          # ビジネスロジック
│   │       └── userService.ts
│   ├── stores/            # Svelteストア(状態管理)
│   │   └── auth.ts
│   ├── types/             # 型定義
│   │   └── index.ts
│   └── utils/
│       ├── format.ts
│       └── validation.ts
├── routes/
│   ├── (app)/             # 認証必要なルート(グループ)
│   │   ├── +layout.svelte
│   │   ├── dashboard/
│   │   └── settings/
│   ├── (auth)/            # 認証系ルート(グループ)
│   │   ├── login/
│   │   └── register/
│   ├── api/
│   │   └── v1/
│   │       └── users/
│   └── +layout.svelte
├── hooks.server.ts    # サーバーサイドhooks
├── hooks.client.ts    # クライアントサイドhooks
├── app.d.ts           # 型定義
├── app.html
└── app.css

重要なポイント

ディレクトリ用途
$libsrc/libのエイリアス、インポートが楽になる
$lib/serverサーバー専用コード(クライアントにバンドルされない)
(group)URLに影響しないルートグループ化
[param]動的ルート

hooks(ミドルウェア)

SvelteKitにはhooksがあり、ミドルウェア的な役割を果たします。

hooks.server.ts(最も使う)

すべてのサーバーリクエストを処理する前に実行されます。

// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  // リクエスト前の処理
  const session = await getSession(event.cookies);
  event.locals.user = session?.user;

  // 認証チェック
  if (event.url.pathname.startsWith('/dashboard') && !event.locals.user) {
    return new Response('Redirect', {
      status: 303,
      headers: { Location: '/login' }
    });
  }

  // 次の処理へ
  const response = await resolve(event);

  // レスポンス後の処理(ヘッダー追加など)
  response.headers.set('X-Custom-Header', 'value');

  return response;
};

複数のhookを組み合わせる

import { sequence } from '@sveltejs/kit/hooks';

const auth: Handle = async ({ event, resolve }) => {
  // 認証処理
  return resolve(event);
};

const logger: Handle = async ({ event, resolve }) => {
  console.log(`${event.request.method} ${event.url.pathname}`);
  return resolve(event);
};

// 順番に実行
export const handle = sequence(logger, auth);

event.localsの型定義

// src/app.d.ts
declare global {
  namespace App {
    interface Locals {
      user: User | null;
    }
  }
}

export {};

型安全なAPI通信

SvelteKitでも型安全なAPI通信を実現できます。

SvelteKitのネイティブ機能

+page.server.tsload関数は自動的に型推論されます。

// +page.server.ts
export async function load() {
  const users = await db.getUsers();
  return { users };  // 型が自動推論される
}
<!-- +page.svelte -->
<script lang="ts">
  export let data;  // { users: User[] } と型推論される
</script>

tRPCを使用する場合

npm install @trpc/server @trpc/client trpc-sveltekit
// lib/trpc/router.ts
import { t } from './trpc';

export const router = t.router({
  getUsers: t.procedure.query(async () => {
    return await db.getUsers();
  }),
  createUser: t.procedure
    .input(z.object({ name: z.string() }))
    .mutation(async ({ input }) => {
      return await db.createUser(input);
    }),
});

export type AppRouter = typeof router;
<!-- +page.svelte -->
<script lang="ts">
  import { trpc } from '$lib/trpc/client';

  // 完全に型安全
  const users = trpc.getUsers.query();
</script>

Hono RPC(軽量な代替)

// バックエンド
import { Hono } from 'hono';

const app = new Hono()
  .get('/users', async (c) => {
    const users = await db.getUsers();
    return c.json(users);
  })
  .post('/users', async (c) => {
    const body = await c.req.json();
    return c.json(await db.createUser(body));
  });

export type AppType = typeof app;
// フロントエンド
import { hc } from 'hono/client';
import type { AppType } from './server';

const client = hc<AppType>('/api');
const res = await client.users.$get();  // 型安全

比較

方法型安全追加依存複雑さ
SvelteKit load関数なし
tRPCあり
Hono RPCあり

シンプルに始めたい場合はSvelteKitのload関数で十分です。API設計を厳密にしたい場合はtRPC、軽量でシンプルな場合はHono RPCがおすすめです。

モノレポでの開発

SvelteKitはフルスタックフレームワークなので、FEとBEを同じプロジェクト内で完結できます。

一体型(小〜中規模向け)

my-app/
├── src/
│   ├── routes/          # ページ(FE)
│   │   ├── +page.svelte
│   │   └── api/         # APIエンドポイント(BE)
│   │       └── users/+server.ts
│   └── lib/             # 共有コード
├── package.json
└── svelte.config.js

分離型モノレポ(大規模向け)

monorepo/
├── apps/
│   ├── web/             # Svelte/React等(FE)
│   └── api/             # Express/Hono等(BE)
├── packages/
│   └── shared/          # 共有型・ユーティリティ
├── package.json
└── pnpm-workspace.yaml  # or turbo.json

適したユースケース

Svelteは以下の場面で特に力を発揮します。

  • パフォーマンスが重要なアプリケーション
  • 小〜中規模のプロジェクト
  • 素早いプロトタイピング
  • バンドルサイズを抑えたい場合

参考文献

まとめ

Svelteはコンパイラベースのアプローチにより、小さいバンドルサイズと高いパフォーマンスを実現するフレームワークです。SvelteKitを使えば、SSR/SSG、APIエンドポイント、型安全なデータ取得などフルスタック開発に必要な機能が揃っています。

学習コストも低く、シンプルなコードで開発できるため、新規プロジェクトの選択肢として検討する価値があります。

CATEGORY
TAGS
円