home / skills / charleswiltgen / axiom / axiom-swiftui-containers-ref
/.claude-plugin/plugins/axiom/skills/axiom-swiftui-containers-ref
This skill helps you master SwiftUI containers and scroll enhancements across stacks, grids, outlines, and lists with practical usage patterns.
npx playbooks add skill charleswiltgen/axiom --skill axiom-swiftui-containers-refReview the files below or copy the command above to add this skill to your agents.
---
name: axiom-swiftui-containers-ref
description: Reference — SwiftUI stacks, grids, outlines, and scroll enhancements through iOS 26
license: MIT
metadata:
version: "1.2.0"
---
# SwiftUI Containers Reference
Stacks, grids, outlines, and scroll enhancements. iOS 14 through iOS 26.
**Sources**: WWDC 2020-10031, 2022-10056, 2023-10148, 2024-10144, 2025-256
## Quick Decision
| Use Case | Container | iOS |
|----------|-----------|-----|
| Fixed views vertical/horizontal | VStack / HStack | 13+ |
| Overlapping views | ZStack | 13+ |
| Large scrollable list | LazyVStack / LazyHStack | 14+ |
| Multi-column grid | LazyVGrid | 14+ |
| Multi-row grid (horizontal) | LazyHGrid | 14+ |
| Static grid, precise alignment | Grid | 16+ |
| Hierarchical data (tree) | List with `children:` | 14+ |
| Custom hierarchies | OutlineGroup | 14+ |
| Show/hide content | DisclosureGroup | 14+ |
---
## Part 1: Stacks
### VStack, HStack, ZStack
```swift
VStack(alignment: .leading, spacing: 12) {
Text("Title")
Text("Subtitle")
}
HStack(alignment: .top, spacing: 8) {
Image(systemName: "star")
Text("Rating")
}
ZStack(alignment: .bottomTrailing) {
Image("photo")
Badge()
}
```
**ZStack alignments**: `.center` (default), `.top`, `.bottom`, `.leading`, `.trailing`, `.topLeading`, `.topTrailing`, `.bottomLeading`, `.bottomTrailing`
### Spacer
```swift
HStack {
Text("Left")
Spacer()
Text("Right")
}
Spacer(minLength: 20) // Minimum size
```
---
### LazyVStack, LazyHStack (iOS 14+)
Render children only when visible. Use inside ScrollView.
```swift
ScrollView {
LazyVStack(spacing: 0) {
ForEach(items) { item in
ItemRow(item: item)
}
}
}
```
### Pinned Section Headers
```swift
ScrollView {
LazyVStack(pinnedViews: [.sectionHeaders]) {
ForEach(sections) { section in
Section(header: SectionHeader(section)) {
ForEach(section.items) { item in
ItemRow(item: item)
}
}
}
}
}
```
---
## Part 2: Grids
### Grid (iOS 16+)
Non-lazy grid with precise alignment. Loads all views at once.
```swift
Grid(alignment: .leading, horizontalSpacing: 10, verticalSpacing: 10) {
GridRow {
Text("Name")
TextField("Enter name", text: $name)
}
GridRow {
Text("Email")
TextField("Enter email", text: $email)
}
}
```
**Modifiers**:
- `gridCellColumns(_:)` — Span multiple columns
- `gridColumnAlignment(_:)` — Override column alignment
```swift
Grid {
GridRow {
Text("Header").gridCellColumns(2)
}
GridRow {
Text("Left")
Text("Right").gridColumnAlignment(.trailing)
}
}
```
---
### LazyVGrid (iOS 14+)
Vertical-scrolling grid. Define **columns**; rows grow unbounded.
```swift
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
ScrollView {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(items) { item in
ItemCard(item: item)
}
}
}
```
### LazyHGrid (iOS 14+)
Horizontal-scrolling grid. Define **rows**; columns grow unbounded.
```swift
let rows = [GridItem(.fixed(100)), GridItem(.fixed(100))]
ScrollView(.horizontal) {
LazyHGrid(rows: rows, spacing: 16) {
ForEach(items) { item in
ItemCard(item: item)
}
}
}
```
### GridItem.Size
| Size | Behavior |
|------|----------|
| `.fixed(CGFloat)` | Exact width/height |
| `.flexible(minimum:maximum:)` | Fills space equally |
| `.adaptive(minimum:maximum:)` | Creates as many as fit |
```swift
// Adaptive: responsive column count
let columns = [GridItem(.adaptive(minimum: 150))]
```
---
## Part 3: Outlines
### List with Hierarchical Data (iOS 14+)
```swift
struct FileItem: Identifiable {
let id = UUID()
var name: String
var children: [FileItem]? // nil = leaf
}
List(files, children: \.children) { file in
Label(file.name, systemImage: file.children != nil ? "folder" : "doc")
}
.listStyle(.sidebar)
```
### OutlineGroup (iOS 14+)
For custom hierarchical layouts outside List.
```swift
List {
ForEach(canvases) { canvas in
Section(header: Text(canvas.name)) {
OutlineGroup(canvas.graphics, children: \.children) { graphic in
GraphicRow(graphic: graphic)
}
}
}
}
```
### DisclosureGroup (iOS 14+)
```swift
@State private var isExpanded = false
DisclosureGroup("Advanced Options", isExpanded: $isExpanded) {
Toggle("Enable Feature", isOn: $feature)
Slider(value: $intensity)
}
```
---
## Part 4: Common Patterns
### Photo Grid
```swift
let columns = [GridItem(.adaptive(minimum: 100), spacing: 2)]
ScrollView {
LazyVGrid(columns: columns, spacing: 2) {
ForEach(photos) { photo in
AsyncImage(url: photo.thumbnailURL) { image in
image.resizable().aspectRatio(1, contentMode: .fill)
} placeholder: { Color.gray }
.aspectRatio(1, contentMode: .fill)
.clipped()
}
}
}
```
### Horizontal Carousel
```swift
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 16) {
ForEach(items) { item in
CarouselCard(item: item).frame(width: 280)
}
}
.padding(.horizontal)
}
```
### File Browser
```swift
List(selection: $selection) {
OutlineGroup(rootItems, children: \.children) { item in
Label {
Text(item.name)
} icon: {
Image(systemName: item.children != nil ? "folder.fill" : "doc.fill")
}
}
}
.listStyle(.sidebar)
```
---
## Part 5: Performance
### When to Use Lazy
| Size | Scrollable? | Use |
|------|-------------|-----|
| 1-20 | No | VStack/HStack |
| 1-20 | Yes | VStack/HStack in ScrollView |
| 20-100 | Yes | LazyVStack/LazyHStack |
| 100+ | Yes | LazyVStack/LazyHStack or List |
| Grid <50 | No | Grid |
| Grid 50+ | Yes | LazyVGrid/LazyHGrid |
**Cache GridItem arrays** — define outside body:
```swift
struct ContentView: View {
let columns = [GridItem(.adaptive(minimum: 150))] // ✅
var body: some View {
LazyVGrid(columns: columns) { ... }
}
}
```
### iOS 26 Performance
- **6x faster list loading** for 100k+ items
- **16x faster list updates**
- **Reduced dropped frames** in scrolling
- **Nested ScrollViews with lazy stacks** now properly defer loading:
```swift
ScrollView(.horizontal) {
LazyHStack {
ForEach(photoSets) { set in
ScrollView(.vertical) {
LazyVStack {
ForEach(set.photos) { PhotoView(photo: $0) }
}
}
}
}
}
```
---
## Part 6: Scroll Enhancements
### containerRelativeFrame (iOS 17+)
Size views relative to scroll container.
```swift
ScrollView(.horizontal) {
LazyHStack {
ForEach(cards) { card in
CardView(card: card)
.containerRelativeFrame(.horizontal, count: 3, span: 1, spacing: 16)
}
}
}
```
### scrollTargetLayout (iOS 17+)
Enable snapping.
```swift
ScrollView(.horizontal) {
LazyHStack {
ForEach(items) { ItemCard(item: $0) }
}
.scrollTargetLayout()
}
.scrollTargetBehavior(.viewAligned)
```
### scrollPosition (iOS 17+)
Track topmost visible item. **Requires `.id()` on each item.**
```swift
@State private var position: Item.ID?
ScrollView {
LazyVStack {
ForEach(items) { item in
ItemRow(item: item).id(item.id)
}
}
}
.scrollPosition(id: $position)
```
### scrollTransition (iOS 17+)
```swift
.scrollTransition { content, phase in
content
.opacity(1 - abs(phase.value) * 0.5)
.scaleEffect(phase.isIdentity ? 1.0 : 0.75)
}
```
### onScrollGeometryChange (iOS 18+)
```swift
.onScrollGeometryChange(for: Bool.self) { geo in
geo.contentOffset.y < geo.contentInsets.top
} action: { _, isTop in
showBackButton = !isTop
}
```
### onScrollVisibilityChange (iOS 18+)
```swift
VideoPlayer(player: player)
.onScrollVisibilityChange(threshold: 0.2) { visible in
visible ? player.play() : player.pause()
}
```
---
## Resources
**WWDC**: 2020-10031, 2022-10056, 2023-10148, 2024-10144, 2025-256
**Docs**: /swiftui/lazyvstack, /swiftui/lazyvgrid, /swiftui/lazyhgrid, /swiftui/grid, /swiftui/outlinegroup, /swiftui/disclosuregroup
**Skills**: axiom-swiftui-layout, axiom-swiftui-layout-ref, axiom-swiftui-nav, axiom-swiftui-26-ref
This skill is a compact reference for SwiftUI container patterns through iOS 26, covering stacks, grids, outlines, and modern scroll enhancements. It distills practical APIs, performance guidance, and code fragments for building responsive layouts on xOS. Use it to choose the right container and optimize scrolling and hierarchy rendering.
The reference catalogs container types (VStack/HStack/ZStack, Lazy stacks, Grid, LazyVGrid/LazyHGrid, List/OutlineGroup, DisclosureGroup) and maps them to typical use cases and iOS availability. It highlights key modifiers and lifecycle behaviors (pinning, lazy loading, grid spanning) and explains scroll innovations introduced in iOS 17–18 such as scrollTargetLayout, scrollPosition, scrollTransition, and onScrollGeometryChange. Performance guidance shows when to prefer lazy containers and how to cache GridItem arrays.
When should I use Grid vs LazyVGrid?
Use Grid for small, static forms requiring exact alignment (loads all views). Use LazyVGrid for larger, scrollable, or dynamic collections to defer view creation.
How do I track the topmost visible item?
Give each item an .id() and use .scrollPosition(id: $state) to update a bound identifier for the topmost visible item.