How it works
LazyVGrid arranges its child views in a grid that grows vertically, filling a fixed set of columns and adding rows as needed. You define the column structure once with an array of GridItem values, and the grid places each successive child into the next available cell, wrapping to a new row when the columns are full. Because it's lazy, LazyVGrid only creates and lays out the cells that are currently visible, which makes it the right choice for long, scrollable collections of items where an eager layout would be wasteful. Reach for it inside a vertically scrolling container whenever you need column-aligned content rather than a single stacked column.
Describe the columns with an array of GridItem
LazyVGrid's layout is driven by its columns parameter: an array of GridItem values, one per column, where the array's count fixes how many columns the grid has. Each GridItem carries a sizing rule such as .flexible(), .fixed(_:), or .adaptive(minimum:). The example declares three flexible columns with
[GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())], so available width is divided evenly into three tracks.Construct the grid and supply its content
The initializer LazyVGrid(columns:alignment:spacing:pinnedViews:content:) takes the column array and a trailing view builder. Child views in the closure flow left-to-right into the columns and wrap onto new rows automatically. Here
LazyVGrid(columns: columns)hands in thecolumnsarray, and the content closure produces the cells that fill each track.Control row spacing and cell alignment
The spacing parameter sets the gap between the rows the grid generates, while the alignment parameter (default .center) positions each child horizontally within its column track. The example passes
spacing: 12to separate successive rows; raising or lowering this value changes only the inter-row gutters, independent of the spacing handled by the GridItem columns themselves.Pair it with a scrolling container
LazyVGrid does no scrolling of its own and produces no rows for content that hasn't been requested, so it's meant to live inside a scroll view that drives that demand. Wrapping it in
ScrollViewlets the grid extend below the visible area and create cells lazily as the user scrolls, which is the arrangement that makes the lazy behavior pay off.Apply modifiers to the grid as a whole
Because LazyVGrid conforms to View, layout modifiers attached to it affect the entire grid rather than individual cells. In the example
.padding()insets the whole grid from the edges of its container; per-cell styling such as.frame,.background, and.cornerRadiusis instead applied to the views produced inside the content closure.
GridItem(.flexible()) to the columns array and watch the same children reflow into four narrower columns instead of three.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct LazyVGridDemo: View {
let columns = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 12) {
ForEach(1..<13) { i in
Text("\(i)")
.frame(maxWidth: .infinity, minHeight: 60)
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
}
}
.padding()
}
}
}