SwiftUI Patterns

ContentUnavailableView for Empty States

May 10, 2026
4 min read
Featured image for blog post: ContentUnavailableView for Empty States

iOS 17 introduced ContentUnavailableView for showing empty states, errors, and search results. Here's how to use it effectively.

Follow along with the code: iOS-Practice on GitHub

Basic Usage

The simplest form takes a title and SF Symbol:

ContentUnavailableView("No Users", systemImage: "person.slash")

This renders a centered, styled view with an icon and text.

Full Customization

For more control, use the full initializer:

ContentUnavailableView {
    Label("Error", systemImage: "exclamationmark.triangle")
} description: {
    Text(error.localizedDescription)
} actions: {
    Button("Retry") {
        Task { await loadUsers() }
    }
    .buttonStyle(.borderedProminent)
}

Label - The main title with icon Description - Supporting text explaining the state Actions - Buttons for user actions (retry, create new, etc.)

Common Patterns

Empty Search Results

ContentUnavailableView.search(text: searchText)

Built-in convenience for "No results for X" with a magnifying glass icon.

Empty List

ContentUnavailableView("No Items", systemImage: "tray")

Error State with Retry

ContentUnavailableView {
    Label("Connection Error", systemImage: "wifi.slash")
} description: {
    Text("Check your internet connection and try again.")
} actions: {
    Button("Retry", action: retry)
        .buttonStyle(.borderedProminent)
}

Permission Required

ContentUnavailableView {
    Label("Photos Access Required", systemImage: "photo.on.rectangle.angled")
} description: {
    Text("Allow access to your photos to continue.")
} actions: {
    Button("Open Settings") {
        // Open app settings
    }
}

Integration with Lists

Use .overlay to show ContentUnavailableView over an empty list:

List(items) { item in
    ItemRow(item: item)
}
.overlay {
    if items.isEmpty && !isLoading {
        ContentUnavailableView("No Items", systemImage: "tray")
    }
}

Or use conditional rendering:

if items.isEmpty {
    ContentUnavailableView("No Items", systemImage: "tray")
} else {
    List(items) { item in
        ItemRow(item: item)
    }
}

Search Integration

Combine with .searchable for filtered results:

@State private var searchText = ""

var filteredItems: [Item] {
    if searchText.isEmpty { return items }
    return items.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}

var body: some View {
    List(filteredItems) { item in
        ItemRow(item: item)
    }
    .searchable(text: $searchText)
    .overlay {
        if filteredItems.isEmpty && !searchText.isEmpty {
            ContentUnavailableView.search(text: searchText)
        }
    }
}

Before iOS 17

If supporting older versions, create a similar view:

struct EmptyStateView: View {
    let title: String
    let systemImage: String
    var description: String?

    var body: some View {
        VStack(spacing: 12) {
            Image(systemName: systemImage)
                .font(.system(size: 48))
                .foregroundColor(.secondary)
            Text(title)
                .font(.headline)
            if let description {
                Text(description)
                    .font(.subheadline)
                    .foregroundColor(.secondary)
            }
        }
    }
}

Interview Tip

ContentUnavailableView is the modern, consistent way to handle empty states. Mentioning it shows you're current with SwiftUI best practices.

Originally published on pixelper.com

© 2026 Christopher Moore / Dead Pixel Studio

Let's work together

Professional discovery, design, and complete technical coverage for your ideas

Get in touch