← All courses

Layout: Stacks, Spacers & Alignment

🗓 May 31, 2026 ⏱ 2 min read

The three stacks

SwiftUI layout is built around three container views. Almost every screen is some combination of them:

  • VStack — arranges children vertically (top to bottom).
  • HStack — arranges children horizontally (left to right).
  • ZStack — layers children on top of each other (for overlays/backgrounds).
VStack(alignment: .leading, spacing: 8) {
    Text("Anand Gaur").font(.headline)
    HStack {
        Image(systemName: "star.fill").foregroundStyle(.yellow)
        Text("Pro member")
    }
}

Spacing and alignment

Each stack takes a spacing: (gap between children) and an alignment: (how children line up on the cross axis). For a VStack, alignment is horizontal (.leading, .center, .trailing); for an HStack, it’s vertical (.top, .center, .bottom).

Spacer: flexible empty space

A Spacer expands to fill available space, pushing other views apart. It’s the key to many layouts.

HStack {
    Text("Total")
    Spacer()            // pushes the price to the far right
    Text("$49").bold()
}

ZStack for layering

ZStack(alignment: .bottomTrailing) {
    Image("banner").resizable().scaledToFill()
    Text("NEW")
        .padding(6)
        .background(.black.opacity(0.6))
        .foregroundStyle(.white)
        .padding(8)
}

Frames: controlling size

Use .frame() to set or constrain a view’s size. maxWidth: .infinity makes a view stretch to fill its parent’s width — very common for full-width buttons and cards.

Text("Full width")
    .frame(maxWidth: .infinity)
    .padding()
    .background(.purple)
    .foregroundStyle(.white)

Scrolling content

If content might exceed the screen, wrap it in a ScrollView:

ScrollView {
    VStack(spacing: 12) {
        ForEach(0..<20) { i in Text("Row \(i)") }
    }
    .padding()
}

GeometryReader (advanced, use sparingly)

When you need to know the available size (e.g. to make something half the screen wide), GeometryReader gives you the dimensions. It’s powerful but can complicate layouts, so reach for stacks, Spacer and .frame first.

Common mistakes

  • Confusing spacing (gap between children) with alignment (cross-axis position).
  • Forgetting .resizable() on images (they show at full natural size).
  • Overusing GeometryReader when a Spacer or .frame(maxWidth:) would do.
Summary: Compose layouts with VStack/HStack/ZStack, control gaps with spacing and cross-axis position with alignment, fill space with Spacer and .frame(maxWidth:.infinity), and wrap long content in a ScrollView.