Control Flow

Render dynamic lists and repeated content with the <for> component.

Overview

Melony provides a <for> component that lets AI render lists of items dynamically. It's perfect for displaying arrays of data like tasks, users, products, or any repeating content.

Basic Usage

The AI can use the <for> component with an items prop containing a JSON array:

<card title="Task List">
  <for items='[{"task": "Buy groceries"}, {"task": "Walk dog"}, {"task": "Code review"}]'>
    <text value="{{item.task}}" />
  </for>
</card>

This renders three text components, one for each task in the array.

Loop Variables

Inside a <for> component, you have access to these special variables:

{{item}} - The current array item

{{index}} - Current index (0-based)

{{isFirst}} - Boolean, true if first item

{{isLast}} - Boolean, true if last item

{{isEven}} - Boolean, true if even index

{{isOdd}} - Boolean, true if odd index

Using Loop Variables

Here's an example using multiple loop variables:

<card title="Shopping List">
  <for items='[
    {"item": "Apples", "qty": 5},
    {"item": "Bread", "qty": 2},
    {"item": "Milk", "qty": 1}
  ]'>
    <row gap="md" align="center">
      <text value="{{index + 1}}." weight="bold" />
      <text value="{{item.item}}" flex="1" />
      <badge label="Qty: {{item.qty}}" />
      <text value="{{isLast ? 'Last item' : ''}}" size="sm" color="muted" />
    </row>
  </for>
</card>

Nested Objects

Access nested properties using dot notation:

<for items='[
  {"user": {"name": "Alice", "age": 30}},
  {"user": {"name": "Bob", "age": 25}}
]'>
  <card>
    <text value="{{item.user.name}}" size="lg" weight="bold" />
    <text value="Age: {{item.user.age}}" color="muted" />
  </card>
</for>

Conditional Styling

Use loop variables for conditional rendering:

<for items='[
  {"status": "completed", "task": "Design"},
  {"status": "pending", "task": "Development"},
  {"status": "completed", "task": "Testing"}
]'>
  <row gap="md">
    <text value="{{index + 1}}" weight="bold" />
    <text value="{{item.task}}" flex="1" />
    <badge 
      label="{{item.status}}" 
      variant="{{item.status === 'completed' ? 'success' : 'warning'}}" 
    />
  </row>
</for>

With Actions

Combine <for> with actions for interactive lists:

<card title="User List">
  <for items='[
    {"id": "1", "name": "Alice"},
    {"id": "2", "name": "Bob"},
    {"id": "3", "name": "Charlie"}
  ]'>
    <row gap="md" align="center">
      <text value="{{item.name}}" flex="1" />
      <button 
        label="View Profile" 
        variant="outline"
        action='{"type":"view-user","id":"{{item.id}}"}' 
      />
      <button 
        label="Delete" 
        variant="destructive"
        action='{"type":"delete-user","id":"{{item.id}}"}' 
      />
    </row>
  </for>
</card>

Complex Example

Here's a more complex example with multiple nested components:

<for items='[
  {
    "project": "Website Redesign",
    "tasks": 3,
    "completed": 2,
    "priority": "high"
  },
  {
    "project": "Mobile App",
    "tasks": 5,
    "completed": 1,
    "priority": "medium"
  }
]'>
  <card>
    <row gap="sm" align="center">
      <text value="{{item.project}}" size="lg" weight="bold" flex="1" />
      <badge 
        label="{{item.priority}}" 
        variant="{{item.priority === 'high' ? 'danger' : 'warning'}}" 
      />
    </row>
    
    <row gap="md">
      <column>
        <text value="Tasks" size="sm" color="muted" />
        <text value="{{item.tasks}}" weight="bold" />
      </column>
      <column>
        <text value="Completed" size="sm" color="muted" />
        <text value="{{item.completed}}" weight="bold" />
      </column>
      <column>
        <text value="Progress" size="sm" color="muted" />
        <text value="{{Math.round(item.completed / item.tasks * 100)}}%" weight="bold" />
      </column>
    </row>
    
    <button 
      label="View Details" 
      variant="primary"
      action='{"type":"view-project","name":"{{item.project}}"}' 
    />
  </card>
</for>

Empty States

Handle empty arrays gracefully:

<card title="Tasks">
  <for items='[]'>
    <text value="{{item.task}}" />
  </for>
  <text value="No tasks yet" color="muted" />
</card>

When the array is empty, the <for> component renders nothing.

Best Practices

  • Use Arrays: Always provide a valid JSON array to the items prop
  • Template Variables: Use {{item.propertyName}} to access item properties
  • Unique Keys: Include unique IDs in your items for better rendering
  • Keep It Simple: Avoid deeply nested loops when possible
  • Error Handling: Handle empty arrays and missing properties

Limitations

  • The items prop must be a valid JSON array string
  • Template expressions have limited JavaScript support
  • Nested <for> loops are not currently supported
  • Items must be objects or primitive values