SwiftUI offers arbitrary geometry through Layout, and laziness through LazyVStack / LazyVGrid, but not both at once. Layout is eager by construction: sizeThatFits(proposal:subviews:cache:) hands you LayoutSubview values you have to measure, and a view you haven't built can't be measured. Asked about lazy custom layouts at the WWDC26 SwiftUI Group Lab, Apple's answer was that the protocol is eager-only and to file a Feedback.
LazyLayoutKit takes the narrower path: require the caller to supply each item's layout input as data, before any view exists.
public protocol LazyLayoutAlgorithm: Equatable, Sendable {
associatedtype Item: Equatable & Sendable
func layout(items: [Item], containerWidth: Double) -> LazyLayoutResult
}
Item is an associated type deliberately: masonry wants an aspect ratio, a timeline wants an interval, a calendar wants a date range. Fixing it to a height would have made this a masonry library with a general-sounding name.
Two implementation notes that might interest people here:
Visibility is answered by a uniform bucket index in compressed-sparse-row form, built over whatever frames the layout emitted. It assumes nothing about structure, so frames may overlap, arrive unordered or use negative coordinates. Items spanning many buckets go on a separate oversized list rather than being duplicated into each, keeping worst-case memory linear. On device the query measures ~2µs and stays flat from 100k to 1M items.
Scroll anchoring keys on caller-supplied identity rather than index, because inserting at the front shifts every index and index-based anchoring would silently hold a different item. Notably, the lookup is a sequential scan, not a hash table: building a 100k-entry table costs ~1.4ms on an M4 but ~29ms on an A16, and anchoring needs exactly one lookup per snapshot.
Measured on an iPhone 14 Pro, Release: 120fps at both 100k and 1M items, 8.34ms p99, zero frames over 16.7ms, ~54 cells materialized at any depth. LazyVStack over the same 100k scored the same — one run each over different content, so read it as generality costing little rather than a performance claim.
Scope is deliberately narrow for 0.1: vertical scrolling only, sizes known up front, collection changes correct but unanimated, exact eager snapshots. All stated in the README.
Feedback, contributions and ideas very welcome!