ArcMoon 1.1.0

Start

Language

Reference

Nothing matches “”.

↑↓ move Enter open Esc close

Language

Loops

[for-each]: lists, the index i, objects, data from files, nested loops, and live loops that follow a signal.

[for-each]

[for-each] writes its body once for each item of a list. Try it: add a name and press Run:

index.arcm
${ const names = ["Adam", "Eid", "Ismail"]; }$
[ul]
  [for-each = ${ names }$, as: "name"]
    [li]${ name }$[end]
  [end]
[end]
Result
  • The list comes from ${ }$ and is read when the page is built.
  • as names the item. Without it, the item is called value.
  • For a list that changes in the browser, use a live loop.

The index: i

i is the item's position, starting at 0:

[for-each = ${ ["Gold", "Silver", "Bronze"] }$]
  [p = data-place: ${ i }$]${ i + 1 }$. ${ value }$[end]
[end]
HTML ArcMoon builds
<p data-place="0">1. Gold</p>
<p data-place="1">2. Silver</p>
<p data-place="2">3. Bronze</p>

Lists of objects

Each item can be an object. Use its fields in text and in props:

${ const users = [{ name: "Adam", role: "admin" }, { name: "Elmi", role: "editor" }]; }$
[for-each = ${ users }$, as: "user"]
  [p = class: ${ user.role }$]${ user.name }$[end]
[end]
HTML ArcMoon builds
<p class="admin">Adam</p>
<p class="editor">Elmi</p>

Data from a file

Import a JSON file in ${ }$ and loop over it:

posts.json
[
  { "title": "Hello, ArcMoon", "date": "2026-10-01" },
  { "title": "Loops in pages", "date": "2026-10-13" }
]
index.arcm
${ import posts from "./posts.json"; }$
[for-each = ${ posts }$, as: "post"]
  [article]
    [h2]${ post.title }$[end]
    [time]${ post.date }$[end]
  [end]
[end]
HTML ArcMoon builds
<article>
  <h2>Hello, ArcMoon</h2>
  <time>2026-10-01</time>
</article>
<article>
  <h2>Loops in pages</h2>
  <time>2026-10-13</time>
</article>

Nested loops

Loops can go inside loops. The inner i hides the outer one, so keep the outer index in a const first:

${ const rows = [["a", "b"], ["c"]]; }$
[for-each = ${ rows }$, as: "row"]
  ${ const r = i; }$
  [for-each = ${ row }$, as: "cell"]
    [span]${ r }$.${ i }$ ${ cell }$[end]
  [end]
[end]
HTML ArcMoon builds
<span>0.0 a</span>
<span>0.1 b</span>
<span>1.0 c</span>

What you can loop over

  • Arrays, strings, Set and Map: anything JavaScript can loop over with for…of.
  • An object: use Object.entries(obj), then entry[0] is the key and entry[1] the value.
  • A number of times: Array.from({ length: 3 }, (_, n) => n + 1) gives 1, 2, 3.
  • An empty list writes nothing. A plain object or a number writes nothing too, with a warning that says how to loop over it.
${ const prices = { tea: 2, cake: 4 }; }$
[for-each = ${ Object.entries(prices) }$, as: "entry"]
  [p]${ entry[0] }$: ${ entry[1] }$ USD[end]
[end]
HTML ArcMoon builds
<p>tea: 2 USD</p>
<p>cake: 4 USD</p>

Live loops

When the list is runtime ${ }$, the loop runs in the browser too: it keeps one row per item, and the rows follow the list as it changes. Since ArcMoon 1.1:

runtime ${
  import { signal } from "arcmoon/reactive";
  const todos = signal([{ id: 1, title: "Read a book" }, { id: 2, title: "Call Eid" }]);
  const remove = (id) => todos(todos().filter((t) => t.id !== id));
}$
[ul]
  [for-each = runtime ${ todos() }$, as: "todo", key: "id"]
    [li]
      runtime ${ i + 1 }$. runtime ${ todo.title }$
      [button = onclick: runtime ${ () => remove(todo.id) }$]Remove[end]
    [end]
  [end]
[end]
HTML ArcMoon builds
<ul>
  <!--arcm-each:e0--><!--arcm-row:1-->
  <li>
    <!--arcm:t1-->1<!--/arcm-->.
    <!--arcm:t2-->Read a book<!--/arcm-->
    <button data-arcm-in="e3">Remove</button>
  </li>
  <!--arcm-row:2-->
  <li>
    <!--arcm:t1-->2<!--/arcm-->.
    <!--arcm:t2-->Call Eid<!--/arcm-->
    <button data-arcm-in="e3">Remove</button>
  </li>
  <template data-arcm-ref="e0">
    <li>
      <!--arcm:t1--><!--/arcm-->.
      <!--arcm:t2--><!--/arcm-->
      <button data-arcm-in="e3">Remove</button>
    </li>
  </template>
</ul>
  • The list is a live value, usually a signal. Set a new list to change it: todos([...todos(), item]).
  • todo and i exist only in runtime ${ }$ inside the loop.
  • key names a property that tells items apart. Rows stay with their items: removing or reordering items moves the real rows, so an input keeps its focus. Without key, rows follow positions.
  • Rows update every time the list is set, even when an item was changed in place. Events always use the row's current item.

The first rows are in the HTML

When the list starts as a literal (signal([…])) or an exported value, ArcMoon writes the first rows into the HTML. The list shows before the script runs, and search engines see it. In those rows, a value is filled in when it only needs the item, i, exported values and plain JavaScript (Math, String, JSON…). Other values, like another signal or your own function, fill in when the script runs. Events are always added by the script.

Not inside a live loop (yet)

These stop the build with a clear message: components, a live loop inside another, [slot], [script], and arcm-ref (it would be on every row; use arcm-shared-ref and ArcMoon.refs()). Put the loop inside an element, like [ul] or [main].

✗ "todo" only exists in the browser, inside a live [for-each]: use runtime ${ todo }$, not ${ todo }$

Mistakes

With as, value doesn't exist. Use the name you chose:

✗ ReferenceError: value is not defined