さとまたwiki

レイアウト

ページ間で共通のUIを定義する

レイアウトとは?

日常での例え:額縁(フレーム)

額縁

どんな絵を入れても
外枠は同じデザイン

レイアウト

どんなページでも
ヘッダーとフッターは同じ

なぜレイアウトが必要?

レイアウトがないと困ること:

  • 全ページにヘッダーのコードをコピペ(10ページあれば10回!)
  • ヘッダーを修正→全ファイルを手作業で修正
  • 修正漏れでページによってデザインが違う...

レイアウトがあれば:1箇所書くだけで全ページに適用!修正も1箇所だけ。

用語解説

レイアウト:ページの共通部分(枠組み)のこと。ヘッダーやフッターなど。

ヘッダー:ページ上部の共通部分。ロゴやナビゲーションメニューを置く場所。

フッター:ページ下部の共通部分。著作権表示やリンク集を置く場所。

サイドバー:ページ横の共通部分。メニューや補足情報を置く場所。

children:子要素のこと。レイアウトでは「ページの中身」を指す。

レイアウトでできること

✓ ヘッダー、フッター、サイドバーの共通化

✓ ナビゲーションメニューの配置

✓ 認証チェック(ログイン必須ページなど)

✓ ページ遷移時のアニメーション

基本的なレイアウト

+layout.svelte

すべてのページに適用

svelte
<!-- src/routes/+layout.svelte -->
<script>
  let { children } = $props();
</script>

<div class="app">
  <header>
    <nav>
      <a href="/">ホーム</a>
      <a href="/about">アバウト</a>
      <a href="/contact">お問い合わせ</a>
    </nav>
  </header>

  <main>
    {@render children()}  <!-- ここにページが表示される -->
  </main>

  <footer>
    <p>© 2024 My Website</p>
  </footer>
</div>

<style>
  .app {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
  }
  main {
    flex: 1;
  }
</style>
プレビュー
ページ内容
© 2024 My Website

@render children() とは?

Svelte 5の書き方です。ここにページの内容(+page.svelte)が挿入されます。
レイアウトは「枠」、childrenは「中身」と考えてください。

ネストしたレイアウト

レイアウトは入れ子にできる

/dashboard 以下だけ別のレイアウト、という設定ができます。

ディレクトリ構造

レイアウトの継承

text
src/routes/
├── +layout.svelte        # 全ページ共通(ヘッダー・フッター)
├── +page.svelte          # / (トップページ)
├── about/
│   └── +page.svelte      # /about
└── dashboard/
    ├── +layout.svelte    # dashboardだけのレイアウト(サイドバー)
    ├── +page.svelte      # /dashboard
    ├── settings/
    │   └── +page.svelte  # /dashboard/settings
    └── profile/
        └── +page.svelte  # /dashboard/profile

/dashboard/* のページは:
1. routes/+layout.svelte(ルート)
2. routes/dashboard/+layout.svelte(ダッシュボード)
の両方が適用される
プレビュー
// /dashboard/settings の表示
[ルートレイアウト]
[ダッシュボードレイアウト]
[settingsページ]

ダッシュボード用レイアウト

サイドバー付きレイアウト

svelte
<!-- src/routes/dashboard/+layout.svelte -->
<script>
  let { children } = $props();
</script>

<div class="dashboard-layout">
  <aside class="sidebar">
    <nav>
      <a href="/dashboard">ダッシュボード</a>
      <a href="/dashboard/profile">プロフィール</a>
      <a href="/dashboard/settings">設定</a>
    </nav>
  </aside>

  <div class="content">
    {@render children()}
  </div>
</div>

<style>
  .dashboard-layout {
    display: flex;
    min-height: calc(100vh - 120px);
  }
  .sidebar {
    width: 250px;
    background: #f3f4f6;
    padding: 1rem;
  }
  .content {
    flex: 1;
    padding: 1rem;
  }
</style>
プレビュー
ページ内容

レイアウトのリセット

+page@.svelte

親レイアウトを使わない

text
src/routes/
├── +layout.svelte           # 通常のレイアウト
├── +page.svelte             # / (レイアウト適用)
└── login/
    └── +page@.svelte        # /login (レイアウトなし!)

ファイル名を +page@.svelte にすると
ルートレイアウトを含む全レイアウトをリセット

+page@dashboard.svelte なら
dashboardレイアウト以降をリセット
プレビュー
+page@.svelte
// ログインページなど
// ヘッダー不要なページに使う

レイアウトでデータを読み込む

+layout.server.ts

レイアウト用のload関数

typescript
// src/routes/+layout.server.ts
export async function load({ cookies }) {
  const sessionId = cookies.get('session');
  const user = sessionId ? await getUser(sessionId) : null;

  return {
    user  // 全ページで使える
  };
}

// src/routes/+layout.svelte
<script>
  let { data, children } = $props();
</script>

<header>
  {#if data.user}
    <span>ようこそ、{data.user.name}さん</span>
    <a href="/logout">ログアウト</a>
  {:else}
    <a href="/login">ログイン</a>
  {/if}
</header>

{@render children()}

// 子ページからもアクセス可能
// src/routes/dashboard/+page.svelte
<script>
  let { data } = $props();
  // data.userにアクセスできる!
</script>
プレビュー
// +layout.server.ts のデータは
// 子ページにも引き継がれる

グループ(括弧フォルダ)

(folder) とは?

括弧でフォルダ名を囲むと、URLには影響しないグループを作れます。
レイアウトだけを共有したいページをまとめるのに便利です。

グループの使用例

URLに影響しないフォルダ

text
src/routes/
├── (marketing)/           # URLには含まれない
│   ├── +layout.svelte     # マーケティング用レイアウト
│   ├── about/
│   │   └── +page.svelte   # /about
│   └── pricing/
│       └── +page.svelte   # /pricing
│
├── (app)/                 # URLには含まれない
│   ├── +layout.svelte     # アプリ用レイアウト
│   ├── dashboard/
│   │   └── +page.svelte   # /dashboard
│   └── settings/
│       └── +page.svelte   # /settings

(marketing)と(app)で別のレイアウトを適用
でもURLは /about, /dashboard のまま
プレビュー
(marketing)/about → /about
(app)/dashboard → /dashboard
// 括弧はURLに含まれない

実践的な構成例

一般的なWebサイト構成

よくあるレイアウトパターン

text
src/routes/
├── +layout.svelte              # 基本レイアウト(ヘッダー・フッター)
├── +layout.server.ts           # 認証情報の取得
│
├── (public)/                   # 公開ページ
│   ├── +layout.svelte          # シンプルなレイアウト
│   ├── +page.svelte            # / (トップ)
│   ├── about/+page.svelte      # /about
│   └── contact/+page.svelte    # /contact
│
├── (auth)/                     # 認証ページ
│   ├── +layout@.svelte         # レイアウトリセット(ヘッダーなし)
│   ├── login/+page.svelte      # /login
│   └── register/+page.svelte   # /register
│
└── (app)/                      # ログイン後のページ
    ├── +layout.svelte          # サイドバー付きレイアウト
    ├── +layout.server.ts       # 認証チェック(リダイレクト)
    ├── dashboard/+page.svelte  # /dashboard
    ├── profile/+page.svelte    # /profile
    └── settings/+page.svelte   # /settings
プレビュー
(public)
ヘッダー + フッター
(auth)
シンプル(ロゴのみ)
(app)
サイドバー + ヘッダー