SwiftUI Empty State Component
The screen you see when there is nothing to see. Most apps ship a blank view here, which reads as a bug rather than a state.
No messages yet
When someone writes to you, it lands here.
Refresh
Variants: With Action, Plain
Install the free SwiftUI empty state
- Add SwiftKitUI.swift to your app once. It holds the tokens and motion curves.
- Copy SKEmptyState.swift below into your project. It needs nothing else.
Usage
Usage.swift
SKEmptyState(systemImage: "tray", title: "No messages",
message: "When someone writes, it lands here.",
actionTitle: "Refresh") { reload() }Full source · 114 lines · MIT
SKEmptyState.swift
//
// SKEmptyState.swift
// Swift Kit UI
//
// Requires: SwiftKitUI.swift
//
// The screen you see when there is nothing to see. Most apps ship a blank view
// here, which reads as a bug rather than a state.
//
// Usage:
// SKEmptyState(systemImage: "tray", title: "No messages",
// message: "When someone writes, it lands here.",
// actionTitle: "Refresh") { reload() }
//
import SwiftUI
public struct SKEmptyState: View {
private let systemImage: String
private let title: String
private let message: String?
private let actionTitle: String?
private let action: (() -> Void)?
private let tint: Color
@State private var appeared = false
@Environment(\.accessibilityReduceMotion) private var reduceMotion
public init(
systemImage: String,
title: String,
message: String? = nil,
actionTitle: String? = nil,
tint: Color = SK.Palette.accent,
action: (() -> Void)? = nil
) {
self.systemImage = systemImage
self.title = title
self.message = message
self.actionTitle = actionTitle
self.tint = tint
self.action = action
}
public var body: some View {
VStack(spacing: SK.Space.lg) {
Image(systemName: systemImage)
.font(SK.Icon.symbol(44, weight: .regular))
.foregroundStyle(tint)
.frame(width: 92, height: 92)
.background(Circle().fill(tint.opacity(0.10)))
// A small settle on appear, so the state reads as arriving
// rather than as content that failed to load.
.scaleEffect(appeared ? 1 : 0.86)
.opacity(appeared ? 1 : 0)
VStack(spacing: SK.Space.xs) {
Text(title)
.font(SK.Type_.headline)
.foregroundStyle(SK.Palette.label)
if let message {
Text(message)
.font(SK.Type_.callout)
.foregroundStyle(SK.Palette.labelSecondary)
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
}
}
.opacity(appeared ? 1 : 0)
if let actionTitle, let action {
Button(actionTitle) {
action()
SK.Haptic.play(.light)
}
.font(SK.Type_.headline)
.foregroundStyle(.white)
.padding(.horizontal, SK.Space.xl)
.padding(.vertical, SK.Space.md)
.background(Capsule().fill(tint))
.buttonStyle(.skPress)
.opacity(appeared ? 1 : 0)
}
}
.padding(SK.Space.xl)
.frame(maxWidth: 340)
.onAppear {
guard !reduceMotion else { appeared = true; return }
withAnimation(SKMotion.settle.standard.delay(0.05)) { appeared = true }
}
.accessibilityElement(children: .combine)
}
}
#Preview("SKEmptyState") {
VStack(spacing: SK.Space.xxxl) {
SKEmptyState(
systemImage: "tray",
title: "No messages yet",
message: "When someone writes to you, it will show up here.",
actionTitle: "Refresh"
) {}
SKEmptyState(
systemImage: "wifi.slash",
title: "You're offline",
message: "Check your connection and try again.",
tint: SK.Palette.warning
)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(SK.Palette.surfaceSunken)
}
Questions
- Is this SwiftUI empty state free for commercial apps?
- Yes. Swift Kit UI is MIT licensed, so you can use it in personal and commercial apps with no payment or sign-up. Keep the licence notice with the code.
- What does it depend on?
- SKEmptyState.swift depends only on SwiftKitUI.swift, a single foundation file with the design tokens and motion curves. There are no third-party packages.
- Which iOS versions does it support?
- iOS 17 and later, plus macOS 14.
Related components
Building a whole app? The Swift Kit adds auth, payments and onboarding, ready to ship.