Render Many from One
Manually writing HTML for each service doesn’t scale. If you have 50 services, you’d need 50 copies of the same markup. Svelte’s {#each} block solves this — write the template once, render it for every item in an array.
What You’ll Learn
- Use
{#each}to iterate over arrays - Access item data and index
- Render dynamic lists from data
The Basic Each Block
The {#each} syntax iterates over an array:
{#each array as item}
<p>{item}</p>
{/each} For each element in array, Svelte renders the content inside the block, with item representing the current element.
A Simple Example
<script>
const fruits = ['Apple', 'Banana', 'Cherry'];
</script>
<ul>
{#each fruits as fruit}
<li>{fruit}</li>
{/each}
</ul> Output:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul> Three items in the array, three <li> elements rendered.
Render BookIt Services
Let’s apply this to the services page:
<!-- filename: src/routes/services/+page.svelte -->
<script>
const services = [
{
id: '1',
slug: 'lawn-mowing',
name: 'Lawn Mowing',
description: 'Professional lawn mowing service.',
price: 50,
duration: 60
},
{
id: '2',
slug: 'hedge-trimming',
name: 'Hedge Trimming',
description: 'Expert hedge and shrub trimming.',
price: 75,
duration: 90
},
{
id: '3',
slug: 'garden-consultation',
name: 'Garden Consultation',
description: 'One-on-one garden planning advice.',
price: 100,
duration: 60
}
];
</script>
<h1>Our Services</h1>
<div class="services-grid">
{#each services as service}
<article class="service-card">
<h2>{service.name}</h2>
<p>{service.description}</p>
<p class="price">${service.price}</p>
<p class="duration">{service.duration} minutes</p>
<a href="/services/{service.slug}">View Details</a>
</article>
{/each}
</div> One template, three service cards. Add more services to the array, more cards appear automatically.
Accessing the Index
Sometimes you need the position of each item. Add a second parameter:
{#each services as service, index}
<article class="service-card">
<span class="number">{index + 1}</span>
<h2>{service.name}</h2>
<!-- ... -->
</article>
{/each} index is zero-based (0, 1, 2…), so add 1 for display numbering (1, 2, 3…).
Destructuring in Each
If you only need certain properties, destructure directly:
{#each services as { name, price, slug }}
<div class="service-preview">
<h3>{name}</h3>
<span>${price}</span>
<a href="/services/{slug}">Details</a>
</div>
{/each} This keeps the template cleaner when you don’t need all properties.
Empty Arrays
What happens with an empty array?
<script>
const services = [];
</script>
{#each services as service}
<p>{service.name}</p>
{/each} Nothing renders. The each block simply produces zero iterations. Combine with {#if} for empty states:
{#if services.length > 0}
{#each services as service}
<article class="service-card">
<!-- ... -->
</article>
{/each}
{:else}
<p>No services available.</p>
{/if} Or use the {:else} directly in the each block:
{#each services as service}
<article class="service-card">
<!-- ... -->
</article>
{:else}
<p>No services available.</p>
{/each} The {:else} inside {#each} triggers when the array is empty.
Iterating Over Objects
To iterate over object entries:
<script>
const categories = {
'lawn-care': 'Lawn Care',
'tree-care': 'Tree Care',
'consultation': 'Consultation'
};
</script>
{#each Object.entries(categories) as [key, value]}
<button data-category={key}>{value}</button>
{/each} Object.entries() converts the object to an array of [key, value] pairs.
Nested Each Blocks
You can nest loops for complex data:
<script>
const categorizedServices = [
{
category: 'Lawn Care',
services: ['Lawn Mowing', 'Lawn Fertilizing']
},
{
category: 'Tree Care',
services: ['Tree Pruning', 'Tree Removal']
}
];
</script>
{#each categorizedServices as group}
<section>
<h2>{group.category}</h2>
<ul>
{#each group.services as service}
<li>{service}</li>
{/each}
</ul>
</section>
{/each} The outer loop iterates categories; the inner loop iterates services within each category.
Performance Note
Each blocks re-render when the array changes. For large lists or frequent updates, Svelte needs help identifying which items changed. That’s where keys come in — covered in the next lesson.
Common Mistakes
Forgetting the as Keyword
<!-- ❌ Missing 'as' -->
{#each services service}
<!-- ✅ Correct -->
{#each services as service} Treating Each Item as the Array
<!-- ❌ Wrong: 'service' is one item, not the array -->
{#each services as service}
<p>Total: {service.length}</p>
{/each}
<!-- ✅ Correct: access properties of the current item -->
{#each services as service}
<p>{service.name}</p>
{/each} Modifying the Array Inside Each
<!-- ❌ Don't mutate during iteration -->
{#each services as service, index}
<button onclick={() => services.splice(index, 1)}>Delete</button>
{/each} Modifying an array while iterating can cause unexpected behavior. Trigger updates after the block, not inside it.
Summary
The {#each} block transforms arrays into rendered markup. Write the template once, and Svelte repeats it for every item. Combined with conditionals, you can build dynamic, data-driven interfaces.
Key takeaways:
{#each array as item}iterates over arrays- Add
, indexfor position access - Use
{:else}in each blocks for empty state handling
Next Steps
As arrays change — items added, removed, reordered — Svelte needs to know which items are which. Continue with Why Keys Matter to learn about keyed each blocks.