DEV Community

Connie Leung
Connie Leung

Posted on • Edited on

Day 4 - List rendering in Vue 3, Svelte 5 and Angular 19

On day 4, I will show how to render a list of items. Each row also has a Delete button that deletes the item from the list. When the list is updated, the template rerenders the list reactively.

There is a new Item type for all applications. An item has an ID, a label, a purchased flag, and a higher priority flag.

type Item = { 
        id: number; 
        label: string; 
        purchased: boolean;
        higherPriority: boolean; 
}      
Enter fullscreen mode Exit fullscreen mode

Create a Reactive List

  • Vue 3 application

In the script tag of the ShoppingCart component, I will create an items ref with an initial value. Then, the template iterates the array to display each element in a list. Each row has a button that does not do anything. Event handling will be covered at a later day.

let items = ref([
       {
          id: 1,
          label: '10 Apples',
          purchased: false,
          higherPriority: false,
       },
       {
          id: 2,
          label: '5 Bananas',
          purchased: false,
          higherPriority: false,
      },
]);
Enter fullscreen mode Exit fullscreen mode

items is a reactive array that maintains a list of items in a shopping cart. When an item is added or deleted to items, the list is re-rendered in the template.

<script setup lang="ts">
   import { Icon } from "@iconify/vue";
   import { ref } from 'vue'

   type Item = { ... }

   let header = ref('Shopping List App')
   let items = ref([...])
</script>

<template>
   <h1>{{ header }}</h1>
   <ul>
      <div class="list-item" v-for="item in items" :key="item.id">
          <li>{{ item.id }} - {{ item.label }}</li>
          <button class="btn btn-cancel" aria-label="Delete">
             <Icon icon="ic:baseline-remove" />
          </button>
       </div>
   </ul>
</template>

<style scoped>
  div.list-item {
     display: flex
  }

  div.list-item > li {
    margin-right: 0.5rem;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

The v-for directive iterates items to display the id and label properties. The :key tracks the unique item id so that the list does not render unnecessarily.
The delete button is adjacent to the item with a remove icon. This button does nothing because it does not register any event handler.
The script tag imports the Icon component from the iconify/vue library and displays the remove icon using it.
In the scoped style, the list item has a flex layout and a margin between the <li> element and the button.

  • SvelteKit application

I used $state to create a reactive array to store the items in a shopping cart. Then, the template uses the for-each syntax to iterate the array to display the items in a list.

let items = $state([
       {
          id: 1,
          label: '10 Apples',
          purchased: false,
          higherPriority: false,
       },
       {
          id: 2,
          label: '5 Bananas',
          purchased: false,
          higherPriority: false,
      },
]); 
Enter fullscreen mode Exit fullscreen mode
<script setup lang="ts">
   import Icon from "@iconify/svelte";

   type Item = { ... }

   let header = $state('Shopping List App')
   let items = $state([...])
</script>

<template>
   <h1>{ header }</h1>
   <ul>
      {#each items as item (item.id)}
         <div class="list-item">
            <li>{item.id} - {item.label}</li>
           <button class="btn btn-cancel" aria-label="Delete">
              <Icon icon="ic:baseline-remove" />
           </button>
        </div>
      {/each}
    </ul>
</template>

<style>
  div.list-item {
     display: flex
  }

  div.list-item > li {
    margin-right: 0.5rem;
  }
</style>>
Enter fullscreen mode Exit fullscreen mode

The #each built-in block iterates items to display the id and label properties. The alias of the array element is item and the unique key is (item.id).
The delete button is adjacent to the item with a remove icon. This button does nothing because it does not register any event handler.
The script tag imports the Icon component from the iconify/vue library and displays the remove icon when it is used.
Any style within the <style> tag is scoped. Similarly, the list item has a flex layout and there is a margin between the <li> element and the button.

  • Angular 19 application
items = signal<Item[]>([
       {
          id: 1,
          label: '10 Apples',
          purchased: false,
          higherPriority: false,
       },
       {
          id: 2,
          label: '5 Bananas',
          purchased: false,
          higherPriority: false,
      },
]);
Enter fullscreen mode Exit fullscreen mode

I declared an items signal with an initial Item array. The signal function has a generic T, and it is set to Item[]. The @for control-flow syntax is responsible for iterating items and displaying each item in a row.

import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { matRemove } from '@ng-icons/material-icons/baseline';

@Component({
  selector: 'app-shopping-cart',
  imports: [NgIcon],
  viewProviders: [ provideIcons({ matRemove })],
  template: `
      <h1>{{ header() }}</h1>
      <ul>
          @for (item of items(); track item.id) {
            <div class="list-item">
              <li>{{ item.id }} - {{ item.label }}</li>
              <button class="btn btn-cancel" aria-label="Delete">
                <ng-icon name="matRemove"></ng-icon>
              </button>
            </div>
          }
      </ul>
  `,
  styles: `
    div.list-item {
      display: flex;
    }

    div.list-item > li {
      margin-right: 0.5rem;
    }
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShoppingCartComponent {
  header = signal('Shopping List App');
  items = signal<Item[]>([... array element ]);
}
Enter fullscreen mode Exit fullscreen mode

Unlike Vue 3 and Svelte 5, the items signal is initialized in the ShoppingCartComponent class.
I imported NgIcons in the imports array and provideIcons({ matRemove }) in the viewProviders array. The ng-icon directive displays the remove icon on the button.

The @for syntax iterates the items signal to display the item in a list. The track expression is mandatory in @for, and the expression tracks the unique item id.

Moreover, the styles property of the Component decorator can be used to add inline styles. Similarly, div.list-item and div.list-item > li inline styles are applied to the list and the list items.

We have successfully updated the shopping cart component to display items in a list.

Github Repos:

Github Pages:

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

ACI image

ACI.dev: Fully Open-source AI Agent Tool-Use Infra (Composio Alternative)

100% open-source tool-use platform (backend, dev portal, integration library, SDK/MCP) that connects your AI agents to 600+ tools with multi-tenant auth, granular permissions, and access through direct function calling or a unified MCP server.

Check out our GitHub!