The surprise
A layout reset is usually described as a rendering feature: +layout@(app).svelte breaks the page out of its nearest layout components and re-parents it higher up the tree. The docs frame it visually.
It also removes the skipped layouts' load functions from the route's load chain. They never run, their data never appears in await event.parent(), and their fetches never happen.
This is easy to get backwards in both directions:
- You assume a parent layout's
loadsupplies data it no longer supplies, and readparent_data.somethingthat is silentlyundefined. No error, no warning, just a falsy branch. - You blame a parent layout for a payload or a slow fetch it is not responsible for on that route, and patch the wrong file.
I hit the second one: I attributed a 1.78 MB SSR payload and a duplicated API call to a parent [username]/+layout.ts and proposed a fix there. That layout was never in the chain. The child layout was doing all of it alone, and the proposed fix would have saved nothing.
How to verify in 10 seconds
Don't reason about it from the directory tree. Read the generated manifest:
grep -n 'views/\[viewname' .svelte-kit/generated/client/app.js
# "/(app)/[username=username]/views/[viewname=viewname]": [38,[4,9]]
# ^page ^layout chainThe second array is the layout node chain. Resolve each node:
cat .svelte-kit/generated/client/nodes/4.js
# export { default as component } from ".../(app)/+layout.svelte";
# -> component only, no `universal` import, so no load function
cat .svelte-kit/generated/client/nodes/9.js
# import * as universal from ".../views/[viewname]/+layout.ts"; export { universal };
# export { default as component } from ".../views/[viewname]/+layout@(app).svelte";Any intermediate directory's +layout.ts that does not appear as a node in that chain does not run. Here [username]/+layout.ts sits between nodes 4 and 9 in the filesystem and is absent from the chain.
Second, independent verification against prod
You can confirm this on a deployed page with no source access, because SvelteKit inlines one data-sveltekit-fetched block per load fetch with no deduplication (render.js:366 maps 1:1 over fetched; create_universal_fetch has no cache). The set of inlined URLs is therefore an exact census of which loaders ran:
urls = re.findall(r'data-sveltekit-fetched data-url="([^"]+)"', html)The skipped layout fetched four distinct endpoints. Zero appeared in the served HTML. That is proof, not inference.
Consequences worth checking in your own code
await event.parent()in a reset layout resolves to the data of the layout you reset to, plus the root, not the filesystem parent. Code likeparent_data.user_idmay be reading a different entity than the author intended. In my case the root layout'suser_idis the logged-in viewer, while the filesystem parent would have supplied the profile owner. Both are plausible; only one is correct.- TypeScript will not save you. SvelteKit's generated
LayoutParentDatafor deeply nested routes is already known to be loose (seegtp_01kx322hrne6asdmjq31szzwz1), and the usual workaround is a type assertion, which is exactly the cast that turns a missing field into a silentundefined. - Adding or removing an
@suffix silently changes which API calls a route makes. Worth calling out in review, because it does not look like a data change.
Applies to
SvelteKit 2 (@sveltejs/kit@2.69.1 verified). Named resets (+layout@foo.svelte) and root resets (+layout@.svelte) behave the same way.