SwiftUI.cc
Snippets
LayoutStacksBeginner

SwiftUI VStack Spacing and Alignment

VStack arranges children top to bottom and sizes itself to fit them. Control the gap with the spacing parameter and the horizontal placement with alignment.

4 min readUpdated 2026-06
Receipt summary with mixed alignment
import SwiftUI

struct ReceiptSummary: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Order #2048")
                .font(.headline)
            VStack(alignment: .leading, spacing: 4) {
                Text("2 × Flat White")
                Text("1 × Almond Croissant")
            }
            .font(.subheadline)
            .foregroundStyle(.secondary)
            Divider()
            HStack {
                Text("Total")
                Spacer()
                Text("$14.50").bold()
            }
        }
        .padding()
        .background(.quaternary.opacity(0.4), in: .rect(cornerRadius: 16))
        .padding()
    }
}

VStack spacing and alignment preview

How VStack sizes itself

A VStack asks each child for its ideal size, stacks them vertically, and then reports the combined footprint to its own parent. It does not scroll and it does not fill the screen by default — a stack is exactly as big as its content. That makes it predictable: if you see extra empty space, it is coming from a frame, Spacer, or the parent, not from the stack itself.

Spacing

VStack(spacing:) controls the gap between every adjacent pair of children. Leaving it as the default nil lets the platform pick a context-aware value, which usually looks right next to system controls. Use spacing: 0 when you need children to touch, such as custom segmented backgrounds or stacked dividers.

VStack(spacing: 0) {
    Color.teal.frame(height: 60)
    Color.indigo.frame(height: 60)
}

Alignment

The alignment parameter takes a HorizontalAlignment.leading, .center (default), or .trailing. It only matters when children have different widths. A common beginner surprise is setting .leading and seeing no change because every child already spans the same width; add a border to each child while debugging to see their true frames.

Common mistakes

  • Reaching for VStack + ForEach with hundreds of rows — initialization cost grows linearly, so switch to LazyVStack.
  • Using nested stacks to fake table layouts that Grid handles with aligned columns.
  • Adding Spacer() to "push" content when a frame(maxHeight: .infinity, alignment: .top) states the intent more directly.

Related reference