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 , index for 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.