Skip to main content

{#snippet ...}

{#snippet name()}...{/snippet}
{#snippet name(param1, param2, paramN)}...{/snippet}

Les snippets, ainsi que les balises de rendu (render tags) sont un moyen de créer des morceaux de markup réutilisables dans vos composants. Plutôt que d’écrire du code dupliqué comme ceci...

{#each images as image}
	{#if image.href}
		<a href={image.href}>
			<figure>
				<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
				<figcaption>{image.caption}</figcaption>
			</figure>
		</a>
	{:else}
		<figure>
			<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
			<figcaption>{image.caption}</figcaption>
		</figure>
	{/if}
{/each}

... vous pouvez écrire ceci :

{#snippet figure(image)}
	<figure>
		<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
		<figcaption>{image.caption}</figcaption>
	</figure>
{/snippet}

{#each images as image}
	{#if image.href}
		<a href={image.href}>
			{@render figure(image)}
		</a>
	{:else}
		{@render figure(image)}
	{/if}
{/each}

Comme avec les déclarations de fonctions, les snippets peuvent avoir un nombre arbitraire de paramètres, qui peuvent avoir des valeurs par défaut, et vous pouvez déstructurer chaque paramètre. Vous ne pouvez cependant pas utiliser de paramètre de reste.

Portée de snippet

Les snippets peuvent être déclarés n’importe où dans votre composant. Ils peuvent référencer des valeurs déclarées en dehors de leur définition, par exemple dans une balise <script> ou un bloc {#each ...} (demo)...

<script>
	let { message = `ça me fait plaisir de te voir` } = $props();
</script>

{#snippet hello(name)}
	<p>salut {name} ! {message} !</p>
{/snippet}

{@render hello('alice')}
{@render hello('bob')}

... et ils sont “visibles” par tout ce qui se trouve dans la même portée lexicale (c-à-d les balises soeurs, et les enfants de ces balises) :

<div>
	{#snippet x()}
		{#snippet y()}...{/snippet}

		<!-- pas de souci -->
		{@render y()}
	{/snippet}

	<!-- ceci va générer une erreur, puisque `y` est hors de portée -->
	{@render y()}
</div>

<!-- ceci va aussi générer une erreur, puisque `x` est hors de portée -->
{@render x()}

Les snippets peuvent se référencer eux-mêmes et les uns les autres (demo) :

{#snippet blastoff()}
	<span>🚀</span>
{/snippet}

{#snippet countdown(n)}
	{#if n > 0}
		<span>{n}...</span>
		{@render countdown(n - 1)}
	{:else}
		{@render blastoff()}
	{/if}
{/snippet}

{@render countdown(10)}

Passer des snippets aux composants

Au sein du template, les snippets sont des valeurs comme les autres. Ainsi, ils peuvent être passés aux composants en tant que props (demo) :

<script>
	import Table from './Table.svelte';

	const fruits = [
		{ name: 'pommes', qty: 5, price: 2 },
		{ name: 'bananes', qty: 10, price: 1 },
		{ name: 'cerises', qty: 20, price: 0.5 }
	];
</script>

{#snippet header()}
	<th>fruit</th>
	<th>qté</th>
	<th>prix</th>
	<th>total</th>
{/snippet}

{#snippet row(d)}
	<td>{d.name}</td>
	<td>{d.qty}</td>
	<td>{d.price}</td>
	<td>{d.qty * d.price}</td>
{/snippet}

<Table data={fruits} {header} {row} />

On peut voir cette technique comme une façon de passer du contenu à un composant, plutôt que des données. Le concept est similaire à celui des slots pour les composants web.

Pour simplifier l’écriture, les snippets déclarés directement à l’intérieur d’un composant deviennent implicitement des props sur le composant (demo) :

<!-- ceci est sémantiquement la même chose qu'au-dessus -->
<Table data={fruits}>
	{#snippet header()}
		<th>fruit</th>
		<th>qté</th>
		<th>prix</th>
		<th>total</th>
	{/snippet}

	{#snippet row(d)}
		<td>{d.name}</td>
		<td>{d.qty}</td>
		<td>{d.price}</td>
		<td>{d.qty * d.price}</td>
	{/snippet}
</Table>

Tout contenu à l’intérieur d’une balise de composant qui n’est pas une déclaration de snippet fait implicitement partie du snippet children (demo) :

App
<Button>cliquez-moi</Button>
Button
<script>
	let { children } = $props();
</script>

<!-- le résultat sera <button>cliquez-moi</button> -->
<button>{@render children()}</button>
<script lang="ts">
	let { children } = $props();
</script>

<!-- le résultat sera <button>cliquez-moi</button> -->
<button>{@render children()}</button>

Notez que vous ne pouvez pas avoir une prop appelée children si vous avez également du contenu au sein du composant — pour cette raison, vous devriez éviter de définir des props avec ce nom.

Vous pouvez déclarer des props de snippet comme étant optionnelles. Vous pouvez soit utiliser le chaînage optionnel pour ne rien afficher du tout si le snippet n’est pas défini...

<script>
	let { children } = $props();
</script>

{@render children?.()}

... soit utiliser un bloc #if pour afficher du contenu par défaut :

<script>
	let { children } = $props();
</script>

{#if children}
	{@render children()}
{:else}
		contenu par défaut
{/if}

Typer les snippets

Les snippets implémentent l’interface Snippet importée depuis 'svelte' :

<script lang="ts">
	import type { Snippet } from 'svelte';

	interface Props {
		data: any[];
		children: Snippet;
		row: Snippet<[any]>;
	}

	let { data, children, row }: Props = $props();
</script>

Avec ce changement, des petits gribouillis rouges vont apparaître si vous essayez d’utiliser le composant sans fournir une prop data et un snippet row. Notez que le type d’argument fourni à Snippet est un tuple, puisque les snippets peuvent avoir plusieurs paramètres.

Nous pouvons restreindre encore un peu le typage en déclarant un générique, de sorte que data et row soient toujours du même type :

<script lang="ts" generics="T">
	import type { Snippet } from 'svelte';

	let {
		data,
		children,
		row
	}: {
		data: T[];
		children: Snippet;
		row: Snippet<[T]>;
	} = $props();
</script>

Exporter des snippets

Les snippets déclarés à la racine d’un fichier .svelte peuvent être exportés depuis un <script module> pour qu’ils soient utilisés par d’autres composants, tant qu’ils ne référencent pas de déclarations présentes dans un <script> non “module” (que ce soit directement ou indirectement, vis d’autres snippets) (demo) :

<script module>
	export { add };
</script>

{#snippet add(a, b)}
	{a} + {b} = {a + b}
{/snippet}

Ceci nécessite Svelte 5.5.0 ou plus récent

Snippets programmatiques

Les snippets peuvent être créés programmatiquement avec l’API createRawSnippet. Cela ne concerne que des cas avancés.

Snippets et slots

En Svelte 4, du contenu peut être fourni aux composants en utilisant des slots. Les snippets sont plus puissants et plus polyvalents, de sorte que les slots sont dépréciés avec Svelte 5.

Modifier cette page sur Github

précédent suivant