Open rChaoz opened 1 month ago
This would be something you can do already:
// $lib/layout-slots.svelte.js
import { getContext, setContext } from 'svelte';
const key = Symbol('layout-slots');
export function initSlots() {
const slots = $state({});
return setContext(key, slots);
}
export function setSlots(slots) {
const context = getContext(key);
Object.assign(context, slots);
}
<!-- +layout.svelte -->
<script>
import { initSlots } from '$lib/layout-slots.svelte.js';
let { children } = $props();
const slots = initSlots();
</script>
<!--
run page logic first to get slot snippets,
the page should have no output.
-->
{@render children()}
<h1>{@render slots.heading()}</h1>
{@render slots.children()}
<!-- +page.svelte -->
<script>
import { setSlots } from '$lib/layout-slots.svelte.js';
setSlots({ heading, children });
</script>
{#snippet heading()}
Hello there
{/snippet}
{#snippet children()}
Main slot content
{/snippet}
Describe the problem
This has been discussed a lot in the past (for example #627), but now with Svelte 5 released, I think we can take a look at this from a different angle, considering all the new Svelte 5 toys.
Describe the proposed solution
Let's consider a rather simple usecase - header that changes based on page. Most pages would want to use the same (default) header, while some can customize it. This could be achieved with snippets or with additional
+page-<name>.svelte
files, for example+page-appbar.svelte
.With snippets (demo)
Allow pages to export snippets:
that can then be used in layouts:
Pros: Code sharing between app bar and page, easy to use Cons: Not SSR-friendly. The page needs to first render before the snippet is created, which then needs to be sent to the layout for re-rendering.
With additional files (demo)
Allow placing additional
+page
files that can be used in layouts:+page.svelte
+page-appbar.svelte
Pros: SSR-friendly, intuitive (
+page
goes tochildren
,+page-x
goes tox
) Cons: Need a communication mechanism to synchronize the page and named slots, as they don't share code.Both of these method can sort-of be achieved in user land, but neither of them is great.
With magic (not demo)
Syntactically I think this is great, but it would need a bit (more) help from the Svelte compiler to work.
For this to work, a
div
withdisplay: contents
would need to be created that would hold the header/footer content, which is rendered inside the snippet, and a virtual component with this div as its target would be passed to the page:Conclusions
This is pretty unhinged, but would it be doable?