Skip to content

Add support for Set objects in x-for loop function#4672

Merged
calebporzio merged 3 commits intoalpinejs:mainfrom
alfanzain:feat/x-for-handle-set-key-value
Feb 9, 2026
Merged

Add support for Set objects in x-for loop function#4672
calebporzio merged 3 commits intoalpinejs:mainfrom
alfanzain:feat/x-for-handle-set-key-value

Conversation

@alfanzain
Copy link
Contributor

Description

x-for doesn't handle Set.
We are using Object.entries(items). Set does not store values as enumerable object keys.

This PR adds the x-for to convert into key-value if the items is a Set object, then pass it into the existing Object.entries(items).

References

alfanzain and others added 3 commits September 2, 2025 12:19
- Convert Set/Map to Array early instead of treating Set as an object
- Sets flow through the array path (semantically correct — Sets are iterables, not key-value stores)
- Maps convert to [[key, value], ...] arrays, enabling natural destructuring
- Added Cypress tests for both Set and Map iteration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@calebporzio
Copy link
Collaborator

PR Review: #4672 — Add support for Set objects in x-for loop function

Type: Feature
Verdict: Merge (with fixes applied)

What's happening (plain English)

  1. A user writes x-for="item in mySet" where mySet is a JavaScript Set
  2. Inside x-for, the isObject() check returns true for Sets (because typeof new Set() === 'object' and it's not an array)
  3. The code enters the object branch and calls Object.entries(items) on the Set
  4. Object.entries() on a Set returns [] — Sets don't have enumerable string-keyed properties
  5. Result: nothing renders. No error, no warning — just silent emptiness

The contributor's original fix converted the Set into a plain object with numeric keys ({0: "foo", 1: "bar"}) and routed it through the object path. That works but it's semantically wrong — a Set is an iterable, not a key-value store. Users expect x-for="item in mySet" to work like x-for="item in [...mySet]".

Other approaches considered

  1. Convert Set to Array early (chosen approach): Array.from(set) before the isObject check. Sets flow through the existing array path naturally. Two lines of code. Also handles Map — Array.from(map) gives [[key, value], ...] which supports destructuring: x-for="[key, value] in myMap".

  2. Contributor's approach (replaced): Convert Set to {0: value, 1: value, ...} inside the isObject branch. This works but gives users fake numeric string keys for something that has no keys. It also doesn't handle Map.

  3. Don't support it — let users do [...mySet]: Valid stance, but Vue 3 supports Set/Map in v-for, the silent failure is bad DX, and the fix is 2 lines. Worth supporting.

Changes Made

  • Replaced the contributor's approach entirely. Removed the Object.fromEntries(Array.from(items).map(...)) conversion inside the isObject block. Instead, added two lines before the isObject check to convert Set and Map to arrays early:
    if (items instanceof Set) items = Array.from(items)
    if (items instanceof Map) items = Array.from(items)
  • Added Map support — the contributor only handled Set. Maps naturally convert to [[key, value], ...] arrays.
  • Added Cypress tests for both Set and Map iteration.
  • Verified regression: both tests fail on main (no spans rendered), pass with the fix.

Test Results

  • x-for.spec.js: 33/33 passing (includes 2 new tests)
  • Regression verified: both new tests fail without the fix
  • CI on original PR: passing

Code Review

The fix is 2 lines in x-for.js:57-58. It follows the existing pattern of early normalization (like the numeric literal support on line 50-52 and the undefined check on line 54). Clean and minimal.

One consideration: Array.from(map) produces [[key, value], ...] entries. Users would need to use array destructuring (x-for="[key, value] in myMap") to access both the key and value. This is intuitive and matches how for...of works with Maps natively.

Security

No security concerns identified.

Verdict

Merge. The bug is real (silent empty render for Sets), the fix is minimal (2 lines), and it follows Alpine's existing early-normalization pattern. I rewrote the contributor's approach to be semantically correct (Sets as arrays, not fake objects) and added Map support as a natural extension. Tests are clean and regression is verified.


Reviewed by Claude

@calebporzio calebporzio merged commit 210e457 into alpinejs:main Feb 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants