List Rendering

Render lists of data using the <template pp-for> directive.

Basic Iteration

Use the item in items syntax to loop through arrays. The content inside the template is repeated for each entry.

shopping-list.html
<ul class="list-disc pl-6">
  <template pp-for="fruit in fruits">
    <li>{fruit}</li>
  </template>
</ul>

<script type="text/pp">
  const [fruits, setFruits] = pp.state([
    "Apples", 
    "Bananas", 
    "Cherries"
  ]);
</script>

Accessing Index

Need the position of the item? Use the parenthesis syntax: (item, index) in items.

leaderboard.html
<div class="space-y-2">
  <template pp-for="(player, i) in leaderboard">
    <div class="flex justify-between border-b py-2">
      <span>Rank #{i + 1}</span>
      <strong>{player}</strong>
    </div>
  </template>
</div>

<script type="text/pp">
  const [leaderboard, setBoard] = pp.state(["Alice", "Bob", "Charlie"]);
</script>

Rendering Objects & Keys

When rendering objects, access properties using dot notation.

Important: Always add a unique key="{id}" attribute to the root element inside the loop. This ensures the framework can efficiently update, reorder, or remove items without bugs.
user-grid.html
<div class="grid grid-cols-3 gap-4">
  <template pp-for="user in users">
    <!-- Unique Key is mandatory for dynamic lists -->
    <div key="{user.id}" class="border p-4 rounded-lg shadow-sm">
      <h4 class="font-bold">{user.name}</h4>
      <p class="text-sm text-gray-500">{user.role}</p>
      
      <button 
        class="mt-2 text-red-500 text-xs hover:underline"
        onclick="removeUser(user.id)"
      >
        Remove User
      </button>
    </div>
  </template>
</div>

<script type="text/pp">
  const [users, setUsers] = pp.state([
    { id: 101, name: "Sarah", role: "Designer" },
    { id: 102, name: "Mike", role: "Developer" },
    { id: 103, name: "Jessica", role: "Manager" }
  ]);

  function removeUser(id) {
    setUsers(users.filter(u => u.id !== id));
  }
</script>

Rendering Dynamic Icons

Just like React, you can store SVG strings or icons in your state and render them dynamically as HTML tags. PulsePoint treats dot-notation tags (e.g., <item.icon />) as dynamic components.

nav-menu.html
<ul class="flex gap-4">
  <template pp-for="link in links">
    <li key="{link.id}" class="flex items-center gap-2">
      <!-- The icon is rendered dynamically from the state string -->
      <link.icon class="size-4 text-muted-foreground" />
      <span>{link.label}</span>
    </li>
  </template>
</ul>

<script type="text/pp">
  // Define your icon as a standard SVG string
  const homeIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>`;
  
  const settingsIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>`;

  const [links, setLinks] = pp.state([
    { id: 1, label: "Home", icon: homeIcon },
    { id: 2, label: "Settings", icon: settingsIcon },
  ]);
</script>
Ref Next: Spread