Skip to main content

高度なルーティング

Restパラメータ

ルートセグメント(route segments)の数がわからない場合は、rest 構文を使用することができます。例えば GitHub のファイルビューアのようなものを実装する場合は…

/[org]/[repo]/tree/[branch]/[...file]

…この場合、/sveltejs/kit/tree/main/documentation/docs/04-advanced-routing.md をリクエストすると、以下のパラメータをページで使うことができます:

{
	org: 'sveltejs',
	repo: 'kit',
	branch: 'main',
	file: 'documentation/docs/04-advanced-routing.md'
}

src/routes/a/[...rest]/z/+page.svelte/a/z にも (つまり、パラメータが全くない場合にも)、/a/b/z/a/b/c/z と同様にマッチします。Rest パラメータの値が正しいことを、例えば matcher を使用するなどして確認してください。

404 pages

Rest パラメータによってカスタムの 404 をレンダリングすることができます。これらのルート(routes)があるとして…

src/routes/
├ marx-brothers/
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte

…もし /marx-brothers/karl にリクエストしても、marx-brothers/+error.svelte ファイルはレンダリング されません 。なぜならどのルート(route) にもマッチしないからです。もしネストしたエラーページをレンダリングしたければ、どんな /marx-brothers/* リクエストにもマッチするルート(route)を作成し、そこから 404 を返すようにしてください:

src/routes/
├ marx-brothers/
| ├ [...path]/
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte
src/routes/marx-brothers/[...path]/+page
import { function error(status: number, body: App.Error): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
} from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */ export function function load(event: any): void
@type{import('./$types').PageLoad}
load
(event: anyevent) {
function error(status: number, body?: {
    message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(404, 'Not Found');
}
import { function error(status: number, body: App.Error): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
} from '@sveltejs/kit';
import type {
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad
} from './$types';
export const const load: PageLoadload:
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad
= (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event) => {
function error(status: number, body?: {
    message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(404, 'Not Found');
};

もし 404 のケースをハンドリングしていない場合、handleError によって表示が行われます。

Optional parameters

[lang]/home というルートに含まれる lang というパラメータは必須です。これらのパラメータをオプションにできると、今回の例では homeen/home のどちらも同じページを指すことができるのでとても便利です。パラメータにもう1つ括弧を付けることでこれができるようになります: [[lang]]/home

optional のルートパラメータ(route parameter)は rest パラメータに続けて使用すること ([...rest]/[[optional]]) はできません。パラメータは ‘greedily’ にマッチし、optional のパラメータは使用されないこともあるためです。

Matching

src/routes/fruits/[page] というルート(route)は /fruits/apple にマッチしますが、/fruits/rocketship にもマッチしてしまいます。これを防ぎたい場合、パラメータ文字列("apple""rocketship")を引数に取ってそれが有効なら true を返す matcherparams ディレクトリに追加することで、ルート(route)のパラメータを適切に定義することができます…

src/params/fruit
/**
 * @param {string} param
 * @return {param is ('apple' | 'orange')}
 * @satisfies {import('@sveltejs/kit').ParamMatcher}
 */
export function function match(param: any): boolean
@paramparam
@return@satisfies{import('@sveltejs/kit').ParamMatcher}
match
(param: any
@paramparam
param
) {
return param: any
@paramparam
param
=== 'apple' || param: any
@paramparam
param
=== 'orange';
}
import type { type ParamMatcher = (param: string) => boolean

The shape of a param matcher. See matching for more info.

ParamMatcher
} from '@sveltejs/kit';
export const const match: (param: string) => param is ("apple" | "orange")match = ((param: stringparam: string): param: stringparam is ('apple' | 'orange') => { return param: stringparam === 'apple' || param: stringparam === 'orange'; }) satisfies type ParamMatcher = (param: string) => boolean

The shape of a param matcher. See matching for more info.

ParamMatcher
;

…そしてルート(routes)を拡張します:

src/routes/fruits/[page=fruit]

もしパス名がマッチしない場合、SvelteKit は (後述のソート順の指定に従って) 他のルートでマッチするか試行し、どれにもマッチしない場合は最終的に 404 を返します。

params ディレクトリにある各モジュールは matcher に対応しています。ただし、matcher のユニットテストに使用される *.test.js*.spec.js ファイルは例外です。

Matcher は サーバーとブラウザの両方で動作します。

ソート(Sorting)

あるパスに対し、マッチするルート(routes)は複数でも構いません。例えば、これらのルート(routes)はどれも /foo-abc にマッチします:

src/routes/[...catchall]/+page.svelte
src/routes/[[a=x]]/+page.svelte
src/routes/[b]/+page.svelte
src/routes/foo-[c]/+page.svelte
src/routes/foo-abc/+page.svelte

SvelteKit は、どのルート(route)に対してリクエストされているのかを判断しなければなりません。そのため、以下のルールに従ってこれらをソートします…

  • より詳細・明確(specific)なルート(routes)ほど、より優先度が高い (例えば、動的なパラメータが1つあるルートより、パラメータのないルートのほうがより詳細・明確(specific)である、など)
  • matchers 付きのパラメータ ([name=type]) は matchers なしのパラメータ ([name]) よりも優先度が高い
  • [[optional]][...rest] パラメータはルート(route)の最後の部分でない限り無視される (最後の部分になっている場合は最も低い優先度として扱われる)。言い換えると、ソートの目的上、x/[[y]]/zx/z は同等に扱われる
  • 優先度が同じ場合はアルファベット順で解決される

…この順序で並べると、/foo-abc の場合は src/routes/foo-abc/+page.svelte を呼び出し、/foo-def の場合は src/routes/foo-[c]/+page.svelte を呼び出します:

src/routes/foo-abc/+page.svelte
src/routes/foo-[c]/+page.svelte
src/routes/[[a=x]]/+page.svelte
src/routes/[b]/+page.svelte
src/routes/[...catchall]/+page.svelte

エンコード(Encoding)

ファイルシステムでは使用できない文字があります — Linux と Mac では /、Windows では \ / : * ? " < > | です。URL においては、#% には特別な意味がありますし、SvelteKit においては [ ] ( ) に特別な意味があります。そのため、これらの文字をそのままルート(route)に使用することはできません。

これらの文字をルート(route)に使用するには、16進数のエスケープシーケンスを使います。[x+nn] というフォーマットで、nn の部分は16進数の文字コードです:

  • \[x+5c]
  • /[x+2f]
  • :[x+3a]
  • *[x+2a]
  • ?[x+3f]
  • "[x+22]
  • <[x+3c]
  • >[x+3e]
  • |[x+7c]
  • #[x+23]
  • %[x+25]
  • [[x+5b]
  • ][x+5d]
  • ([x+28]
  • )[x+29]

例えば、/smileys/:-) というルート(route)を作る場合は、src/routes/smileys/[x+3a]-[x+29]/+page.svelte ファイルを作成します。

JavaScript を使って文字の16進数コードを判定することができます:

':'.String.charCodeAt(index: number): number

Returns the Unicode value of the character at the specified location.

@paramindex The zero-based index of the desired character. If there is no character at the specified index, NaN is returned.
charCodeAt
(0).Number.toString(radix?: number): string

Returns a string representation of an object.

@paramradix Specifies a radix for converting numeric values to strings. This value is only used for numbers.
toString
(16); // '3a', hence '[x+3a]'

また、Unicode のエスケープシーケンスを使用することもできます。通常、エンコードされていない文字を直接使用することができるので、こうする必要はありませんが、何らかの理由で、例えばファイル名に絵文字を使用することができない場合、エスケープ文字を使用することができます。言い換えると、以下は同じことをしているということです:

src/routes/[u+d83e][u+dd2a]/+page.svelte
src/routes/🤪/+page.svelte

Unicode エスケープシーケンスのフォーマットは [u+nnnn] で、nnnn の部分は 0000 から 10ffff までの適切な値です (JavaScript の文字列エスケープとは異なり、ffff 以上のコードポイントを表現するためにサロゲートペアを使用する必要はありません)。Unicode エンコーディングについてもっと知りたい方は、Programming with Unicode を参照してください。

ディレクトリの先頭に . 文字があると、TypeScript で 問題 が起きるため、例えば .well-known のようなルート(route)を作る場合はこれらの文字をエンコードしておくと良いでしょう: src/routes/[x+2e]well-known/...

Advanced layouts

デフォルトでは、 レイアウトの階層ルート(route)の階層 に反映されます。場合によっては、そうしたくないこともあるかもしれません。

(group)

‘アプリ’ のルート(routes)としてのレイアウト (例えば /dashboard/item) が1つあり、’マーケティング’ のルート(routes)としての別のレイアウト (/about/testimonials) があるかもしれません。これらのルート(routes)を、ディレクトリの名前を括弧でくくることでグループ化することができます。通常のディレクトリとは異なり、(app)(marketing) はそれらの中のルート(routes)の URL パス名には影響しません:

src/routes/
│ (app)/
│ ├ dashboard/
│ ├ item/
│ └ +layout.svelte
│ (marketing)/
│ ├ about/
│ ├ testimonials/
│ └ +layout.svelte
├ admin/
└ +layout.svelte

+page(group) の中に直接配置することもできます (例えば、/(app)(marketing) のページであるべき場合など)。

Breaking out of layouts

最上位のレイアウト(root layout)は、アプリの全てのページに適用されます。省略した場合、デフォルトは {@render children()} です。もし、いくつかのページで他のページとは異なるレイアウト階層を持ちたい場合には、アプリ全体を1つまたは複数のグループにして、共通のレイアウトを継承しないルート(route)を分けることができます。

上記の例で、/admin ルート(route)は (app)(marketing) のレイアウトを継承しません。

+page@

ページは、ルート(route)ごとに現在のレイアウト階層から抜け出すことができます。先ほどの例に出てきた (app) グループの中に、/item/[id]/embed ルート(route)があるとします:

src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page.svelte
│ │ │ └ +layout.svelte
│ │ └ +layout.svelte
│ └ +layout.svelte
└ +layout.svelte

通常、これは最上位のレイアウト(root layout)と (app) レイアウトと item レイアウトと [id] レイアウトを継承します。@ と、その後ろにセグメント名 (最上位のレイアウト(root layout)の場合は空文字列(empty string)) を追加することで、これらのレイアウトのどれかにリセットすることができます。この例では、以下のオプションから選択できます:

  • +page@[id].svelte - src/routes/(app)/item/[id]/+layout.svelte を継承します
  • +page@item.svelte - src/routes/(app)/item/+layout.svelte を継承します
  • +page@(app).svelte - src/routes/(app)/+layout.svelte を継承します
  • +page@.svelte - src/routes/+layout.svelte を継承します
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page@(app).svelte
│ │ │ └ +layout.svelte
│ │ └ +layout.svelte
│ └ +layout.svelte
└ +layout.svelte

+layout@

ページと同じように、同じ方法でレイアウト 自体 をその親のレイアウトの階層から外すことができます。例えば、+layout@.svelte コンポーネントはその全ての子ルート(routes)の階層をリセットします。

src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page.svelte  // uses (app)/item/[id]/+layout.svelte
│ │ │ ├ +layout.svelte  // inherits from (app)/item/+layout@.svelte
│ │ │ └ +page.svelte    // uses (app)/item/+layout@.svelte
│ │ └ +layout@.svelte   // inherits from root layout, skipping (app)/+layout.svelte
│ └ +layout.svelte
└ +layout.svelte

レイアウトグループを使うときは

全てのユースケースがレイアウトのグループ化に適しているわけではありませんし、無理に使用する必要もありません。あなたのユースケースが複雑な (group) のネストになってしまうかもしれませんし、たった1つの例外ケースのために (group) を導入したくないかもしれません。コンポジション (再利用可能な load 関数や Svelte コンポーネント) や if 文など、他の手段を使用してやりたいことを実現するのは全く問題ありません。以下の例では、最上位のレイアウト(root layout)に戻し、他のレイアウトでも使用できるコンポーネントや関数を再利用したレイアウトを示しています:

src/routes/nested/route/+layout@
<script>
	import ReusableLayout from '$lib/ReusableLayout.svelte';
	let { data, children } = $props();
</script>

<ReusableLayout {data}>
	{@render children()}
</ReusableLayout>
src/routes/nested/route/+layout
import { function reusableLoad(event: import("@sveltejs/kit").LoadEvent): Promise<Record<string, any>>reusableLoad } from '$lib/reusable-load-function';

/** @type {import('./$types').PageLoad} */
export function function load(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
@type{import('./$types').PageLoad}
load
(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event) {
// Add additional logic here, if needed return function reusableLoad(event: import("@sveltejs/kit").LoadEvent): Promise<Record<string, any>>reusableLoad(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event); }
import { function reusableLoad(event: import("@sveltejs/kit").LoadEvent): Promise<Record<string, any>>reusableLoad } from '$lib/reusable-load-function';
import type { type PageLoad = (event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageLoad } from './$types';

export const const load: PageLoadload: type PageLoad = (event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageLoad = (event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event) => {
	// Add additional logic here, if needed
	return function reusableLoad(event: import("@sveltejs/kit").LoadEvent): Promise<Record<string, any>>reusableLoad(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event);
};

その他の参考情報

Edit this page on GitHub

previous next