sveltekit-data について
SvelteKit 2 のルートデータ、フォームアクション、ページオプションを扱う開発者向けスキルです。サーバーとユニバーサルの load、ストリーミング、再実行制御、レンダリング設定を解説します。
主な機能
- 01ユニバーサル/サーバー load 関数
- 02データ共有と URL 依存関係
- 03ストリーミングと load の無効化
- 04フォームアクションと検証
- 05use:enhance による段階的拡張
- 06プリレンダリングと描画オプション
活用シーン
- 01サーバーとユニバーサルのデータ取得を選択
- 02遅いデータをページ表示と並行して配信
- 03POST フォームの構築と検証
- 04プリレンダリング、SSR、CSR の設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ full-stack-skills/ svelte-skills --skill sveltekit-dataインストール後、Claude Code で/sveltekit-dataを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlexamples/form-actions.mdload-functions.mdpage-options.mdREADME.mdreferences/form-actions-reference.mdload-functions-reference.mdpage-options-reference.mdREADME.mdrerunning-loads-reference.md詳細
SvelteKit Data: Loading, Form Actions, Page Options (SvelteKit 2)
このスキルでは、SvelteKit のデータ層を支える 3 つの柱を扱います:(1)データの読み込み(+page.js / +page.server.js / +layout.js / +layout.server.js の load 関数、page.data、URL データ、cookies/headers、エラーとリダイレクト、ストリーミングレスポンス、依存関係の追跡と手動無効化)、(2)フォームアクション(デフォルト/名前付きアクション、fail による検証、use:enhance によるプログレッシブエンハンスメント、GET と POST)、(3)ページオプション(prerender / entries / ssr / csr / trailingSlash / config)。
このスキルを使う場面
+page.jsと+page.server.jsのどちらを使うか決めるとき(プライベートな認証情報、シリアライズ、組み合わせて使う場合)load関数でparams/route/url/fetch/parent/depends/untrackを使うときpage.dataを使って、親レイアウトから子ページのデータにアクセスするときerror(status, msg)/redirect(status, location)を使って load/action を終了するとき- Promise を返してストリーミングするスケルトン画面を実装するとき
- 「load 関数がいつ再実行されるか」を解決し、
invalidate/invalidateAll/untrackで正確に制御するとき - server
load/ action でcookiesを読み取ったり、レスポンスヘッダーを設定したりするとき +page.server.jsの actions を設計するとき:デフォルトアクション、名前付きアクション、?/nameクエリパラメーター、fail()による検証use:enhanceでフォームをプログレッシブエンハンスし、formフィールドに値を設定し、デフォルトのリセットを防ぎ、カスタムapplyActionを使うとき- どのルートで
prerender = true/entries()/ssr = false/csr = false/trailingSlashを使うか決めるとき
重要:データの読み込み(universal load と server load 関数)
SvelteKit には 2 種類の load 関数があり、実行場所と制約が異なります。
Universal load(+page.js / +layout.js):デフォルトでは、SSR 時にサーバーで 1 回実行され、クライアントの hydration 時にもう 1 回実行されます。その後のすべてのナビゲーションではブラウザー内で実行されます。シリアライズできないオブジェクト(Svelte コンポーネントのコンストラクターなど)を含む、任意の JS 値を返せます。SSR 段階の fetch は HTML にインライン化され、hydration 時に再利用されるため、プライベートな認証情報がクライアントに漏れることはありません。
Server load(+page.server.js / +layout.server.js):常にサーバーで実行されます。戻り値は devalue でシリアライズできなければなりません(JSON + BigInt / Date / Map / Set / RegExp / 循環参照)。cookies / locals / request / clientAddress / platform にアクセスできます。
// +page.server.js
import * as db from '$lib/server/database';
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
return { post: await db.getPost(params.slug) };
}
// +page.js —— 公共 API fetch,浏览器可直接拉
/** @type {import('./$types').PageLoad} */
export async function load({ fetch, params }) {
const res = await fetch(`/api/items/${params.id}`);
return { item: await res.json() };
}
両方を同時に使えます。両方が存在する場合、server load が先に実行され、その戻り値が universal load の data パラメーターになります。ページに渡されるのは universal load の戻り値です。
// +page.server.js —— 仅在服务端,返回 sessionId
export async function load({ locals }) {
return { sessionId: locals.sessionId };
}
// +page.js —— 浏览器也能跑,但能拿到服务端上下文
export async function load({ data, fetch }) {
const res = await fetch(`/api/me`, { headers: { 'x-session': data.sessionId } });
return { me: await res.json() };
}
どちらを使うか:
- プライベートな環境変数、データベース、ファイルシステムが必要 → server load
- データが公開 API のもので、サーバーへの往復を減らしたい → universal load
- シリアライズできないオブジェクト(Svelte コンポーネントの class)を返したい → universal load
- ストリーミング(遅いデータ + スケルトン画面)が必要 → server load が必須(universal load の Promise はストリーミングされません)
- クライアントでも再実行したい → universal load
重要:page.data(コンポーネント間でデータを共有する)
ページとすべての祖先レイアウトはそれぞれ独自の data prop を持ち、自身とすべての祖先のデータを結合したものを含みます。親コンポーネントが子コンポーネントから返されたデータを必要とする場合は、$app/state の page.data を使います($app/stores の $page は旧式の同等機能です)。
<script>
import { page } from '$app/state';
/** @type {import('./$types').LayoutProps} */
let { data, children } = $props();
</script>
<svelte:head>
<title>{page.data.title}</title>
</svelte:head>
{@render children()}
結合ルール:同じ階層で同じ key が現れた場合、後の値が優先されます。+layout.js が { a:1, b:2 } を返し、+page.js が { b:3, c:4 } を返す場合 → data = { a:1, b:3, c:4 }。
重要:URL データ(params/route/url)
load 関数では url / route / params を使って URL にアクセスします。SSR 段階では url.hash は使用できません(サーバーには fragment がありません)。
// src/routes/a/[b]/[...c]/+page.js
/** @type {import('./$types').PageLoad} */
export function load({ url, route, params }) {
// route.id = '/a/[b]/[...c]'
// url = URL 实例(origin/pathname/searchParams/...)
// params.b = 'x' params.c = 'y/z'
return { q: url.searchParams.get('q') };
}
url.searchParams.get/getAll/has は、依存関係の追跡ではそれぞれ独立した key です。?x=1&y=1 → ?x=1&y=2 に変わっても、y だけに依存する load は再実行されません。
重要:Cookies と Headers
cookies の読み取り/書き込みができるのは、server load のみです。setHeaders は universal load 内で SSR 時に呼び出すと有効ですが、ブラウザー内での呼び出しは no-op です。
// +layout.server.js
export async function load({ cookies }) {
const sessionid = cookies.get('sessionid');
return { user: await db.getUser(sessionid) };
}
// +page.js —— 转发上游 cache 头
export async function load({ fetch, setHeaders }) {
const response = await fetch('https://cms.example.com/products.json');
setHeaders({ 'cache-control': response.headers.get('cache-control') });
return response.json();
}
制約:setHeaders では同じ名前の header を 1 回しか設定できません。setHeaders で set-cookie を設定することはできません(cookies.set を使ってください)。fetch が cookies を送信するのは同一オリジンまたはサブドメインへのリクエストのみで、それ以外のドメインでは handleFetch hook を使います。
重要:エラーとリダイレクト
error(status, message) と redirect(status, location) はどちらも例外を直接スローします。自分で throw しないでください(SvelteKit 1.x の動作は非推奨です)。redirect はtry ブロックの中に置かないでください(catch に捕捉されます)。
import { error, redirect } from '@sveltejs/kit';
export function load({ locals }) {
if (!locals.user) error(401, 'not logged in');
if (!locals.user.isAdmin) error(403, 'not an admin');
}
export function load({ locals, url }) {
if (!locals.user) {
const next = url.pathname + url.search;
redirect(303, `/login?redirectTo=${encodeURIComponent(next)}`);
}
}
expected error(error() でスローされたエラー)は、適切な status とともに最も近い +error.svelte に表示されます。unexpected error は handleError hook を呼び出し、500 として処理されます。
ブラウザー側のナビゲーションには $app/navigation の goto を使います。
import { goto } from '$app/navigation';
goto('/login');
重要:Promise によるストリーミング
Server load がawait していない Promise を返すと、その Promise はブラウザーにストリーミングされ、先に高速なデータをレンダリングし、後から遅いデータを表示できます。
// +page.server.js —— 关键数据先 await,慢数据后流
export async function load({ params }) {
return {
post: await loadPost(params.slug),
comments: loadComments(params.slug) // 不 await → 单独流
};
}
<h1>{data.post.title}</h1>
{#await data.comments}
<p>Loading comments...</p>
{:then comments}
{#each comments as c}<p>{c.content}</p>{/each}
{:catch error}
<p>error: {error.message}</p>
{/await}
厳格な制約:
- ストリーミングが有効になるのは、JS が有効で、Lambda/Firebase などのバッファリングを行うプラットフォームではない場合のみです
- レスポンスのストリーミング出力が始まった後は、
setHeaders/redirectを実行できません - 手書きの Promise には
.catch(() => {})を追加して unhandled rejection を防いでください。fetchは SvelteKit が自動的に処理します - ネストされた Promise 内の
params.xは依存関係の追跡対象になりません。トップレベルの body でアクセスする必要があります
重要:load 関数が再実行される条件
SvelteKit は各 load の依存関係を追跡し、不要な再実行を避けます。load が再実行される条件:
paramsのプロパティにアクセスし、その値が変わったurl.pathname/url.searchなどにアクセスし、その値が変わったurl.searchParams.get/getAll/hasに対応するパラメーターが変わったawait parent()を呼び出し、親の load が再実行されたfetch(url)またはdepends(url)で依存関係を宣言し、invalidate(url)が呼び出されたinvalidateAll()がすべてのアクティブな load を強制的に再実行する
// +page.js —— 自定义依赖标签(约定 [a-z]: 前缀)
export async function load({ fetch, depends }) {
depends('app:random');
const r = await fetch('https://api.example.com/random-number');
return { number: await r.json() };
}
<script>
import { invalidate, invalidateAll } from '$app/navigation';
function rerun() {
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate(url => url.href.includes('random-number'));
invalidateAll();
}
</script>
<button onclick={rerun}>refresh</button>
server load は fetch の URL に自動的に依存しません(認証情報の漏えいを防ぐためです)。明示的に depends(url) を指定する必要があります。untrack(fn) で依存関係から除外します:
export async function load({ untrack, url }) {
if (untrack(() => url.pathname === '/')) return { message: 'Welcome!' };
}
再実行 ≠ コンポーネントの再作成。+layout.svelte / +page.svelte のインスタンスは保持され、data prop のみが更新され、コンポーネント内部の state は保持されます。強制的に再作成するには、{#key page.url.pathname} を使います。
重要:フォームアクション(default/named、バリデーション、リダイレクト)
+page.server.js から actions オブジェクトをエクスポートすると、<form> のエンドポイントを提供できます。action は常に POST です(GET で副作用を発生させるべきではありません)。
default action:
// src/routes/login/+page.server.js
/** @satisfies {import('./$types').Actions} */
export const actions = {
default: async (event) => { /* ... */ }
};
<form method="POST">
<input name="email">
<input name="password" type="password">
<button>Log in</button>
</form>
named actions:?/name で区別します:
export const actions = {
login: async (event) => { /* ... */ },
register: async (event) => { /* ... */ }
};
<form method="POST" action="?/login">...</form>
<form method="POST" action="/login?/register">...</form>
<form method="POST" action="?/login">
<button>Login</button>
<button formaction="?/register">Register</button>
</form>
重要:default と named は併用できません。名前付き action への POST がリダイレクトしない場合、?/name が URL に残り、次回の default POST もその action に処理されます。
バリデーションエラー:fail(status, data) はステータスとデータを返し、data は form prop / page.form / page.status に入ります:
import { fail } from '@sveltejs/kit';
export const actions = {
login: async ({ cookies, request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
if (!email) return fail(400, { email, missing: true });
const user = await db.getUser(email);
if (!user || user.password !== db.hash(password)) {
return fail(400, { email, incorrect: true });
}
cookies.set('sessionid', await db.createSession(user), { path: '/' });
return { success: true };
}
};
構成:action は RequestEvent を受け取り、request.formData() の読み取り、cookies への書き込み、return / fail / redirect / error の返却ができます。戻り値は form prop に入りますが、今回のレスポンスのみ有効です(再読み込みすると消えます)。安全に値を再入力するには、ユーザーが入力した許可対象のフィールドのみを返します。パスワードは絶対に返してはいけません。
リダイレクト:load と同様に redirect(status, location) を使います。
action の後の load:action の完了後(リダイレクト / 予期しないエラーの場合を除く)、ページが再レンダリングされ、load 関数が再実行されます。handle hook は再実行されません。handle 内で cookie から locals.user を読み取っている場合、action で cookie を変更した後に event.locals を手動で更新する必要があります。
重要:use:enhance(プログレッシブエンハンスメント)
use:enhance は <form> の action であり、フォームを「JavaScript がなくても動作し、JavaScript がある場合はよりスムーズに」します。method="POST" と +page.server.js の action の組み合わせでのみ使用できます。+server.js や GET に使用するとエラーになります。
最小限の使い方:
<script>
import { enhance } from '$app/forms';
/** @type {import('./$types').PageProps} */
let { form } = $props();
</script>
<form method="POST" use:enhance>
<input name="email" value={form?.email ?? ''}>
</form>
デフォルトの動作:ブラウザー標準の動作を模倣しつつ、ページ全体の再読み込みを避けます。form / page.form / page.status を更新し(同じページへの action の場合のみ。それ以外では更新されません)、<form> をリセットし、成功時に invalidateAll を実行し、リダイレクト時に goto を呼び出し、エラー時に最も近い +error.svelte をレンダリングして、フォーカスをリセットします。
カスタム SubmitFunction(callback を返すと送信後のデフォルト動作が置き換わります。元に戻すには update() または applyAction(result) を使います):
<script>
import { enhance, applyAction } from '$app/forms';
import { goto } from '$app/navigation';
let submitting = $state(false);
/** @type {import('./$types').PageProps} */
let { form } = $props();
</script>
<form
method="POST"
use:enhance={({ formElement, formData, action, cancel, submitter }) => {
submitting = true;
return async ({ result, update }) => {
submitting = false;
if (result.type === 'redirect') {
goto(result.location, { invalidateAll: true });
} else {
await applyAction(result); // 等价于默认 success/failure/redirect/error 行为
}
};
}}
>
<button disabled={submitting}>Save</button>
</form>
すべて手動で実装(use:enhance なし):submit イベント + fetch + deserialize を使います(result に Date / BigInt が含まれるため、JSON.parse は使用できません):
<script>
import { invalidateAll, goto } from '$app/navigation';
import { applyAction, deserialize } from '$app/forms';
/** @param {SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }} e */
async function handleSubmit(e) {
e.preventDefault();
const data = new FormData(e.currentTarget, e.submitter);
const response = await fetch(e.currentTarget.action, {
method: 'POST', body: data,
headers: { 'x-sveltekit-action': 'true' } // 同名 +server.js 时强制走 action
});
const result = deserialize(await response.text());
if (result.type === 'success') await invalidateAll();
applyAction(result);
}
</script>
<form method="POST" onsubmit={handleSubmit}>...</form>
同じルートに +server.js がある場合、fetch はデフォルトで +server.js に送信されます。action に POST するには、header x-sveltekit-action: true を必ず追加してください。
重要:ページオプション(prerender/entries/ssr/csr/trailingSlash)
ページオプションは、ページ全体(またはサブツリー)のレンダリング方法を制御します。+page.js / +page.server.js / +layout.{js,server.js} からエクスポートします。子が親を上書きするため、ルート layout で prerender を有効にし、個別のページで無効にできます。
// +page.js / +layout.js / +page.server.js
export const prerender = true; // 构建时生成 HTML
export const prerender = false; // 显式禁用(用于根 layout 开启全部 prerender 的场景)
export const prerender = 'auto'; // 可 prerender 也可 SSR(不写入 manifest 排除)
export const ssr = false; // 仅 CSR——空 shell
export const csr = false; // 不发任何 JS
export const trailingSlash = 'always' | 'never' | 'ignore';
// entries —— 动态路由告诉 prerender 哪些值
// src/routes/blog/[slug]/+page.server.js
/** @type {import('./$types').EntryGenerator} */
export function entries() {
return [{ slug: 'hello-world' }, { slug: 'another-post' }]; // 可 async
}
// config —— adapter-specific
/** @type {import('some-adapter').Config} */
export const config = { runtime: 'edge' };
prerender:すべてのユーザーに同じ内容を表示する場合(マーケティングサイト/ドキュメント/ブログ)。cookies /url.searchParams/ ユーザー状態 / form action には不適切です(POST には server が必要です)。動的ルートではentries()またはkit.prerender.entriesを使います。エラー「prerenderable としてマークされていますが、prerender されていません」→entries/ link を追加するか、'auto'に変更しますssr = false:ルート layout で設定すると、アプリ全体が SPA になります。ssr = false+csr = false= 何もレンダリングされません。禁止ですcsr = false:<script>が取り除かれ、<form>でuse:enhanceを使用できず、リンクはブラウザー標準の遷移になり、HMR は無効になります。開発時はcsr = dev;にして HMR を維持しますtrailingSlash:'never'(デフォルト)の場合、/about/→ 301 →/about。'always'の場合、prerender の出力はabout/index.html。'ignore'は SEO を損なうため推奨しませんconfigオブジェクトはトップレベルでマージされます(ディープマージはされません)。子 layout / page では変更が必要な key のみ上書きします
よくある問題の解決方法
- 秘密の API が認証情報を公開している →
+page.server.jsに変更するか、universal を介して機密性のないフィールドを中継します dataprop がundefined→let { data } = $props();または型宣言が漏れています- ストリーミングデータが hydration で失われる → universal load の Promise はストリーミング転送されません。
+page.server.jsに変更します - ストリーミング中に
setHeadersがエラーになる → レスポンスのストリーミング開始後は header を変更できません fail()の後にフォームのフィールドが空になる → 第 2 引数に再入力するフィールド({ email })を含め、value={form?.email ?? ''}を指定します- action で cookie を変更した後もページに古いユーザーが表示される →
handleは一度しか実行されません。action 内でevent.locals.user = ...を手動で設定する必要があります use:enhanceが機能しない →method="POST"で、POST 先が+page.server.jsの action である必要があります- 「prerenderable としてマークされていますが、prerender されていません」→
entries()またはkit.prerender.entriesを追加します entries()の位置が違う → 動的パラメーターを持つ末端(+page.js/+page.server.js/+server.js)に置く必要があります。layout には置けませんparent()によるウォーターフォール →parent()に依存しないgetData(params)を先に await してから、await parent()を呼び出します- SSR 段階で
url.hashが undefined → サーバーには fragment がありません。onMountで読み取るように変更します
注意点
- Server load の戻り値はシリアライズ可能である必要があります:
Map/Set/Date/BigInt/RegExp/循環参照は OK(devalue)が、Svelte コンポーネントの class/class instance は返せません(transport hook をカスタマイズする場合を除く) - load 関数は純粋に保つ必要があります——
+page.server.jsのトップレベルでlet userを宣言し、リクエスト間で共有しないでください(マルチテナントの長寿命環境では、状態が漏洩します) - 依存関係の追跡はトップレベルの body にのみ適用されます:await 後の promise 内で
params.xにアクセスしても再実行されません(dev 警告) - searchParams の追跡粒度:
get/getAll/hasは個別に追跡されます。url.searchParamsにアクセスすると、url.search全体へのアクセスと同じく、文字列全体が追跡されます formprop はレスポンスが存在するときのみ有効です——リロードするとクリアされます(今回の送信に対する応答のみで、永続データではありません)use:enhanceはデフォルトではページをまたいでformを更新しません:/aから/b?actionに POST しても、/aの form は更新されません——applyAction(result)が必要です- 同名の
+server.jsと+page.server.jsが競合します:fetch('/x')はデフォルトで+server.jsに送信されます。action に POST するにはx-sveltekit-action: trueヘッダーを追加する必要があります +page.jsが server load を橋渡しします:layout.js がない場合は({ data }) => dataとみなされ、server load のデータが自動的に渡されます- streaming とリダイレクトは競合します:レスポンスのストリームが始まるとヘッダーは変更できません。ストリーミングされる promise 内で
redirectは呼び出せません - Lambda/Firebase はページ全体をバッファリングします——ストリーミングレスポンスは提供されません
trailingSlash: 'ignore'は SEO に悪影響を与えます:/xと/x/は異なる URL ですcsr = falseは HMR と互換性がありません:開発時は一時的にcsr = dev;を有効にしますredirectはtry {...}内で catch されます——redirect()を直接呼び出し、try で囲まないでください- エコーの安全性:form データのパスワード/token は echo せず、許可されたフィールドのみを echo してください
FAQ
Q: +page.js と +page.server.js のどちらを選べばよいですか? A: 秘密の認証情報/DB/cookies → server;公開 API/シリアライズ不可能なオブジェクト → universal。両方を同時に使用でき、server が先に実行されます。
Q: page.data と data prop の違いは何ですか? A: data prop = 現在のコンポーネントとすべての祖先のデータをマージしたもの;page.data = 現在のページが返すデータで、どの祖先からでも読み取れます。
Q: load を強制的に再実行するにはどうすればよいですか? A: invalidate(url) でピンポイントに無効化します(URL または depends タグを基準)。または invalidateAll() ですべて無効化します。server load 内の fetch(url) は URL に自動で依存しないため、depends(url) が必要です。
Q: form の送信後に form prop がなくなりました。 A: 正常です。form prop はレスポンスが存在するときのみ有効です。リロードするとクリアされます。永続データは DB に保存し、load で読み込んでください。
Q: use:enhance と redirect の組み合わせは? A: デフォルトでは goto(result.location) を呼び出します。カスタマイズする場合は、callback 内で result.type === 'redirect' を判定した後、goto(result.location, { invalidateAll: true }) を呼び出します。
Q: 動的ルート /blog/[slug] を prerender するには? A: slug のリストを返す entries() 関数を追加するか、svelte.config.js の kit.prerender.entries で設定します。
Q: app 全体を SPA にするには? A: ルートの +layout.js に export const ssr = false; を設定します(SSG は推奨されません)。
Q: JS を無効にするには? A: export const csr = false;——hydration は行われず、<form> は引き続き動作します(POST でページ全体がリロードされます)。
Q: 同じフォームを異なる action に送信するには? A: <button formaction="?/other"> で <form action> を上書きします。
Q: action のデータ型安全性を確保するには? A: /** @satisfies {import('./$types').Actions} */ アノテーションを使用します。form prop には ActionData 型を使用します。
Q: serverless で streaming は使えますか? A: Lambda/Firebase はページ全体をバッファリングします。NGINX ではバッファリングを無効にする設定が必要です。
Q: parent() は同期ですか、非同期ですか? A: await parent() が必須です。
例とリファレンステーブル
主な API 一覧
| 概念 | 主なファイル/API |
|---|---|
| Universal load | +page.js / +layout.js PageLoad / LayoutLoad |
| Server load | +page.server.js / +layout.server.js PageServerLoad / LayoutServerLoad |
| コンポーネント間でのデータ読み取り | $app/state の page / App.PageData |
| URL データ | params / route.id / url(hash は SSR では利用不可) |
| Cookies/Headers | cookies / setHeaders(server のみ) |
| エラー/リダイレクト | error() / redirect() / goto |
| Streaming | server load が await されていない Promise を返す |
| 依存関係の追跡 | depends / untrack / invalidate / invalidateAll |
| Form actions | actions / fail / redirect / form prop |
| Progressive enhancement | enhance / applyAction / deserialize |
| Page options | prerender / entries / ssr / csr / trailingSlash / config |
ファイルインデックス
| ファイル | トピック | 例の数 |
|---|---|---|
examples/load-functions.md | load 関数の全範囲 | 17 |
examples/form-actions.md | form action + use:enhance | 17 |
examples/page-options.md | page options の全範囲 | 12 |
| ファイル | トピック |
|---|---|
references/load-functions-reference.md | load の完全な API/引数/戻り値/$types |
references/form-actions-reference.md | action API/use:enhance callback/hook の統合 |
references/page-options-reference.md | すべての page options/制約/動作 |
references/rerunning-loads-reference.md | 再実行のタイミング/手動 invalidate/untrack |