SvelteKitで記事一覧・詳細ページを作った話

SvelteKitのチュートリアルを一通り終えた後、実際に手を動かして定着させるために記事一覧・詳細ページを作りました。 この記事では、そこで学んだ動的ルーティングとload関数の基礎をまとめます。

このシリーズの記事

  1. SvelteKitで記事一覧・詳細ページを作った話(この記事)
  2. SvelteKitでTODOアプリを作った話
  3. SvelteKitで認証・セッション管理を実装した話
  4. SvelteKitをCloudflare Pagesにデプロイした話

作ったもの

  • / :記事タイトルの一覧が並ぶページ
  • /posts/1 のように記事IDをURLに含む詳細ページ
  • 存在しないIDにアクセスすると404エラーページ

ファイル構成

src/routes/
  +layout.svelte        # 全ページ共通レイアウト
  +page.svelte          # 記事一覧ページ
  posts/
    [id]/
      +page.svelte      # 記事詳細ページ(UI)
      +page.ts          # 記事詳細のload関数

動的ルーティング:[id] フォルダ

/posts/1/posts/2 のようにURLにパラメータが入るページは、フォルダ名を [id] にするだけで対応できます。

mkdir -p src/routes/posts/[id]

フォルダ名の [id] 部分がそのままパラメータ名になります。[slug][userId] のように自由に命名できます。

一覧ページ:+page.svelte

まず一覧ページにデータを直書きして、リンクを並べます。

<!-- src/routes/+page.svelte -->
<script lang="ts">
  const posts = [
    { id: 1, title: 'SvelteKitとは', body: 'SvelteKitはフルスタックフレームワークです。' },
    { id: 2, title: 'ルーティング入門', body: 'ファイルベースのルーティングを使います。' },
    { id: 3, title: 'load関数の使い方', body: 'データ取得はload関数で行います。' },
  ];
</script>

<h1>記事一覧</h1>

<ul>
  {#each posts as post}
    <li>
      <a href="/posts/{post.id}">{post.title}</a>
    </li>
  {/each}
</ul>

詳細ページ:load関数でデータを渡す

詳細ページはUIとデータ取得を分離します。+page.ts にload関数を書き、+page.svelte で受け取る構成です。

+page.ts(load関数)

// src/routes/posts/[id]/+page.ts
import { error } from "@sveltejs/kit";
import type { PageLoad } from "./$types";

const posts = [
  {
    id: 1,
    title: "SvelteKitとは",
    body: "SvelteKitはフルスタックフレームワークです。",
  },
  {
    id: 2,
    title: "ルーティング入門",
    body: "ファイルベースのルーティングを使います。",
  },
  {
    id: 3,
    title: "load関数の使い方",
    body: "データ取得はload関数で行います。",
  },
];

export const load: PageLoad = ({ params }) => {
  const post = posts.find((p) => p.id === Number(params.id));

  if (!post) error(404, "記事が見つかりません");

  return { post };
};

params.id でURLのパラメータを取得できます。記事が見つからない場合は error() でそのまま404ページに飛ばします。

+page.svelte(UI)

<!-- src/routes/posts/[id]/+page.svelte -->
<script lang="ts">
  let { data } = $props();
</script>

<h1>{data.post.title}</h1>
<p>{data.post.body}</p>

<a href="/">← 一覧に戻る</a>

$props() で受け取った data の中に、load関数が返したオブジェクトがそのまま入っています。

PageLoad 型をつける理由

チュートリアルでは型なしで書くことが多いですが、実際のプロジェクトでは PageLoad を付けるのが推奨です。

// 型なし(動くが補完が効かない)
export function load({ params }) {
  return { post };
}

// 型あり(推奨)
export const load: PageLoad = ({ params }) => {
  return { post };
};

PageLoad はSvelteKitがファイル構成を見て ./$types に自動生成する型です。これを付けることで、params に何が入るか・return した値が data としてどう渡るかをTypeScriptが把握し、誤った使い方をエディタが検出してくれます。

まとめ

概念 内容
[id] フォルダ URLのパラメータに対応する動的ルート
params.id URLのパラメータを取得
+page.ts の load 関数 ページに渡すデータを返す
error(404, ...) エラーページに飛ばす
let { data } = $props() load関数の戻り値を受け取る
PageLoad TypeScriptで型安全にする

load関数をページのUIと分離することで、データの取得と表示の責務がはっきりします。次はこのデータを $lib に切り出して共通化するところを試してみます。


次の記事:SvelteKitでTODOアプリを作った話

← トップページに戻る