Named layouts — solves pathless routes and granular layout resets
See original GitHub issueDescribe the problem
There’s a whole slew of issues surrounding layout resets, such as these:
- https://github.com/sveltejs/kit/issues/4103
- https://github.com/sveltejs/kit/issues/4133
- https://github.com/sveltejs/kit/issues/2647
- https://github.com/sveltejs/kit/issues/1685
There might well be others. The essence of them is that __layout.reset.svelte is too blunt an instrument for the problem it purports to solve — your / page might be unique (but you can’t have a root-level reset), or your nested route might want to reset an intermediate layout but not the root layout, or you might just want to reset a layout for a specific component without the overhead of creating a neighbouring reset component.
A separate issue asks for ‘pathless’ routes, so that your marketing pages and your app itself can both live at the root level, even though they have totally different layouts. But it’s actually the same problem, in disguise.
Describe the proposed solution
Credit for this belongs mostly to @mrkishi, not me. The idea is to have named layouts, and give components a way to declare which layouts they belong to.
We name a layout like so:
routes/
├ __layout.{js,svelte}
└ __layout-special.{js,svelte}
Any page can now mount inside the special layout rather than the default layout by specifying so in its own filename:
routes/
├ __layout.{js,svelte}
├ __layout-special.{js,svelte}
├ normal-page.{js,svelte}
├ abnormal-page#special.{js,svelte}
This applies at arbitrary depths: in this case, /deeply/nested/path will ignore the __layout, deeply/__layout and deeply/nested/__layout files.
routes/
├ __layout.{js,svelte}
├ __layout-special.{js,svelte}
├ deeply
│ ├ nested
│ │ ├ path#special.{js,svelte}
│ │ └ __layout.{js,svelte}
│ └ __layout.{js,svelte}
If a folder specifies a layout name, its contents will inherit it — /deeply/nested/path/contains/more/stuff would use __layout-special as well as any __layout files contained inside path#special:
routes/
├ __layout.{js,svelte}
├ __layout-special.{js,svelte}
├ deeply
│ ├ nested
│ │ ├ path#special
│ │ │ ├ contains
│ │ │ │ ├ more
│ │ │ │ │ └ stuff
At any point, you can reset to a layout further up the tree. This is a horribly contrived example, bear with me — /deeply/nested/path/contains/more/things would use deeply/__layout-x (which itself uses the root __layout), rather than __layout-special specified up-tree:
routes/
├ __layout.{js,svelte}
├ __layout-special.{js,svelte}
├ deeply
│ ├ __layout-x.{js,svelte}
│ ├ nested
│ │ ├ path#special
│ │ │ ├ contains
│ │ │ │ ├ more
│ │ │ │ │ ├ stuff
│ │ │ │ │ └ things#x
The most common cases are probably a) resetting to the default root layout, and b) resetting to no layout (i.e. a completely blank page). We could do these by using ~ as an alias for ‘default’, and omitting a name for ‘complete reset’:
page-that-uses-default-layout#~.svelte
page-that-uses-no-layout#.svelte
A little punctuation-heavy, but it reuses concepts efficiently I think.
We can now see how this solves the issues listed above.
#3832
Since sibling components can now use different layouts, there’s no need to invent syntax for ‘pathless’ routes, and the filesystem continues to match the routing table, rather than having things nested confusingly:
routes/
├ __layout-app.{js,svelte}
├ __layout-auth.{js,svelte}
├ __layout-marketing.{js,svelte}
├ [userId]#app
│ └ profile.svelte
├ dashboard#app.svelte
├ index#marketing.svelte
├ reset-password#auth.svelte
├ signin#auth.svelte
├ signup#auth.svelte
└ product#marketing.svelte
(In reality, you’d designate either app or marketing as the default, so you wouldn’t need to declare names for all these.)
#4103
Instead of this…
login
├ __layout.reset.svelte
└ index.svelte
…we could have this…
login
index#.svelte
…or even this:
login#.svelte
#4133, #2647
Pages can now easily break out of any intermediate layouts between them and the root. The #2647 example, where you want test.svelte to render inside the root layout but not the admin layout…
routes
├ __layout.svelte
├ admin
│ ├ __layout.svelte
│ ├ spec
│ │ ├ __layout.reset.svelte
│ │ └ test.svelte
…is easily achieved:
routes
__layout.svelte
admin
__layout.svelte
spec
test#~.svelte
#1685
The index page can now have a special layout:
__layout.{js,svelte}
__layout-special.{js,svelte}
index#special.svelte
other.svelte
stuff.svelte
Alternatives considered
The alternative people have suggested is exporting something from the page component — export const reset = 2, or export const Layout = SomeComponent.
But we can’t then know which layouts to use or omit without loading the component. Using filenames gives us the ability to figure out all the important stuff at build time.
Importance
would make my life easier
Additional Information
No response
Issue Analytics
- State:
- Created 2 years ago
- Reactions:11
- Comments:20 (14 by maintainers)

Top Related StackOverflow Question
The fact that this would work…
…and this would fail…
…is way too deep in the uncanny valley for my liking.
The reason I’m not too concerned about causing punctuation soup in the file tree is that a) named sections would be the exception, not the rule (I think it’s much more common for layout trees to loosely follow URL structure than for every subtree to be unique), and b) inheritance. Besides, being able to see at a glance that these are my marketing pages and these are my app pages (without needing to open up files and scan for specific exports) is a good thing, I’d argue.
Just a lose idea. Maybe it would make sense to have a single metadata file for a route (page and endpoint) that contains the information that should be available without needing to load the route.
This metadata file could contain the layout overrides discussed here, param validators (https://github.com/sveltejs/kit/issues/4291), and also more meta data like route redirects, etc.
Maybe that would deter too much from SvelteKit’s mentality to keep things straight forward to understand, as reading the file hierarchy isn’t enough anymore to know what route is running when. But then, those extra files (first normal layouts, now special layouts, then route parameters, etc.) introduce only increasing complexity and arguably just as well make it not straight forward anymore to understand what runs when.
I guess it remains to be seen where the tipping point is, when encoding metadata in one dimensional length-an-character-restricted file/folder names becomes more complex than in two dimensional unrestricted code files.