Use native Liquid Glass APIs on iOS 26+. Do not recreate the effect with materials, blurs, or shadows.
Core rules
- Don't build custom Liquid Glass through adding backgrounds, outlines, blurs and shadows.
- Buttons — style: use
.buttonStyle(.glass)for non-colored buttons, and.buttonStyle(.glassProminent)for tinted buttons..glassProminentsupports.tint(), but this can only be a color (not a gradient). - Buttons — shape: use
.buttonBorderShape()instead of applying a shape to the button label by hand. - Buttons — padding: using this button setup adds roughly 13pt of padding inside the liquid glass shape; keep this in mind when adapting designs.
- Custom views: use
.glassEffect(.regular, in: ...)to embed custom Views inside liquid glass containers. - Grouped glass: use
GlassEffectContainerwhen multiple liquid glass elements are next to each other. this View / Container adds a liquid merge effect when the elements grow / touch each other. - Scrolling: avoid using liquid glass inside ScrollView and List (anything that scrolls).
- Bottom bars: when anchoring a liquid glass View to the bottom of the screen, prefer embedding it in
.safeAreaBar(.bottom)instead of a VStack or.overlay(). safeAreaBar adds a subtle blur effect behind its content. - Toolbar items — no glass: on iOS 26+, navigation bar and window toolbar items get a shared Liquid Glass background by default. For items that should not show the glass capsule (plain icons, custom labels, status text, logos), apply
.sharedBackgroundVisibility(.hidden)on theToolbarItem, not on the inner view. - SF Symbols — no pre-enclosed symbols: do not use pre-enclosed SF Symbol variants (such as
plus.circle,xmark.circle.fill, ortrash.circle) inside Liquid Glass buttons or containers. The glass shape already acts as the container; pre-enclosed symbols create an awkward "double enclosure" (a circle inside a circle). Use base/unenclosed symbols (plus,xmark,trash) and let.buttonBorderShape()or the glass container define the enclosing shape.
Decision tree
Need a button?
├─ Neutral / secondary → .buttonStyle(.glass)
└─ Tinted / primary → .buttonStyle(.glassProminent).tint(someColor)
Icon in a glass button / surface?
└─ Plain unenclosed symbol (e.g. "plus", not "plus.circle") — glass provides enclosure
Need a custom non-button surface (chip, badge, card)?
└─ .glassEffect(.regular, in: shape) on the view content
Multiple glass elements nearby?
└─ Wrap in GlassEffectContainer(spacing: ...) { ... }
Fixed bottom toolbar / action bar?
└─ .safeAreaBar(.bottom) { ... } — not VStack + overlay
Toolbar item without glass background?
└─ ToolbarItem { ... }.sharedBackgroundVisibility(.hidden)
Inside ScrollView, List, or Form rows?
└─ Do not use Liquid Glass — use solid/material fallback
Workflow
1) Review existing UI
- Flag custom blur/material stacks masquerading as glass.
- Check buttons use
.glass/.glassProminent, not hand-built capsules. - Confirm
.buttonBorderShape()is used instead of clipping the label. - Verify grouped elements sit in
GlassEffectContainer. - Flag glass inside scrollable containers.
- Check bottom-anchored bars use
.safeAreaBar(.bottom). - Check toolbar items that should appear without glass use
.sharedBackgroundVisibility(.hidden)on theToolbarItem. - Flag pre-enclosed SF Symbols (e.g. circle/square variants) inside glass buttons or surfaces.
- Gate with
#available(iOS 26, *)and provide fallbacks.
2) Implement or refactor
- Pick the right primitive (button style vs
glassEffectvssafeAreaBar). - Apply layout and typography first; add glass modifiers last.
- Wrap adjacent glass elements in
GlassEffectContainer. - Account for ~13pt internal button padding when matching designs.
- Use unenclosed SF Symbols so the glass shape provides the enclosing geometry.
- Add iOS 26 availability checks and pre-26 fallbacks.
Patterns
Glass buttons
// Secondary / neutral
Button("Cancel") { dismiss() }
.buttonStyle(.glass)
.buttonBorderShape(.capsule)
// Primary / tinted — color only, not gradient
Button("Save") { save() }
.buttonStyle(.glassProminent)
.tint(.blue)
.buttonBorderShape(.roundedRectangle(radius: 12))
Do not clip the label yourself:
// ❌ Wrong — shape on label, not the glass button
Button { action() } label: {
Text("Save")
.padding()
.background(.ultraThinMaterial, in: Capsule())
}
// ✅ Right — native glass handles shape and padding
Button("Save") { action() }
.buttonStyle(.glassProminent)
.buttonBorderShape(.capsule)
Do not use pre-enclosed SF Symbols inside glass buttons:
// ❌ Wrong — pre-enclosed symbol creates redundant double enclosure
Button { add() } label: {
Image(systemName: "plus.circle.fill")
}
.buttonStyle(.glass)
.buttonBorderShape(.circle)
// ✅ Right — plain symbol, glass shape provides the enclosure
Button { add() } label: {
Image(systemName: "plus")
}
.buttonStyle(.glass)
.buttonBorderShape(.circle)
Custom glass surfaces
Label("3 items", systemImage: "tray")
.padding(.horizontal, 16)
.padding(.vertical, 10)
.glassEffect(.regular, in: .capsule)
Add .interactive() when the surface responds to touch:
Text("Tap me")
.padding()
.glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16))
Grouped glass (merge effect)
GlassEffectContainer(spacing: 24) {
HStack(spacing: 24) {
ToolButton(icon: "pencil")
ToolButton(icon: "eraser")
ToolButton(icon: "lasso")
}
}
private struct ToolButton: View {
let icon: String
var body: some View {
Image(systemName: icon)
.frame(width: 56, height: 56)
.font(.title2)
.glassEffect(.regular, in: .circle)
}
}
Tune spacing to control how close elements must be before the liquid merge kicks in.
Bottom action bar
ContentView()
.safeAreaBar(.bottom) {
HStack {
Button("Share") { share() }
.buttonStyle(.glass)
Button("Done") { done() }
.buttonStyle(.glassProminent)
}
}
Prefer this over pinning with VStack { Spacer(); ... } or .overlay(alignment: .bottom).
Toolbar items without glass
On iOS 26+, toolbar items in the same logical grouping share a Liquid Glass background. Hide it when the item should look bare:
.toolbar {
ToolbarItem(placement: .principal) {
Text("Draft")
.font(.headline)
}
.sharedBackgroundVisibility(.hidden)
ToolbarItem(placement: .topBarTrailing) {
Button { add() } label: {
Image(systemName: "plus")
}
}
.sharedBackgroundVisibility(.hidden)
ToolbarItem(placement: .topBarTrailing) {
Button("Save") { save() }
.buttonStyle(.glassProminent)
}
}
Apply .sharedBackgroundVisibility(.hidden) on the ToolbarItem, not on the Button or label inside. Hiding the effect places the item in its own grouping, which can change spacing relative to glass-backed neighbors.
Do not put the modifier on the inner view:
// ❌ Wrong — modifier on Button, glass background remains
ToolbarItem(placement: .topBarTrailing) {
Button { add() } label: {
Image(systemName: "plus")
}
.sharedBackgroundVisibility(.hidden)
}
// ✅ Right — modifier on ToolbarItem
ToolbarItem(placement: .topBarTrailing) {
Button { add() } label: {
Image(systemName: "plus")
}
}
.sharedBackgroundVisibility(.hidden)
Availability fallback
if #available(iOS 26, *) {
content.glassEffect(.regular, in: .rect(cornerRadius: 16))
} else {
content.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
}
Anti-patterns
| Anti-pattern | Why it fails | Do instead |
|---|---|---|
.background(.ultraThinMaterial) + blur + stroke + shadow |
Not Liquid Glass; wrong optics and no merge/morph | .glassEffect(...) or button styles |
Gradient .tint() on .glassProminent |
API accepts color only | Solid Color tint, or custom label without glassProminent tint |
Glass on List/ScrollView rows |
Scroll + glass = visual glitches, perf cost | Opaque/material rows; glass only on fixed chrome |
| Shape applied to button label | Misses native padding (~13pt) and border rendering | .buttonBorderShape() |
Bottom bar via .overlay |
No system blur bar treatment | .safeAreaBar(.bottom) |
| Multiple glass views without container | No merge effect, worse rendering | GlassEffectContainer |
| Glass capsule on toolbar items that should be bare | iOS 26 adds shared glass to toolbar groupings by default | .sharedBackgroundVisibility(.hidden) on the ToolbarItem |
.sharedBackgroundVisibility on inner view |
Does not remove toolbar glass background | Apply on ToolbarItem (or other ToolbarContent) |
Pre-enclosed SF Symbols in glass buttons (plus.circle, xmark.circle.fill) |
Redundant double enclosure ("circle in circle"); clashes with glass shape | Use unenclosed symbol (plus, xmark) and let .buttonBorderShape provide the container |
Review checklist
- No hand-rolled blur/material glass imitations
- Buttons use
.glassor.glassProminentwith.buttonBorderShape() - Design spacing accounts for ~13pt internal button padding
- Custom surfaces use
.glassEffect(.regular, in: ...) - Adjacent glass wrapped in
GlassEffectContainer - No glass inside scroll views
- Bottom chrome uses
.safeAreaBar(.bottom) - Toolbar items without glass use
.sharedBackgroundVisibility(.hidden)onToolbarItem - No pre-enclosed SF Symbols in glass buttons (e.g. "plus" instead of "plus.circle")
-
#available(iOS 26, *)with fallback on older OS
Additional resources
- Detailed API notes and morphing transitions: reference.md
- Applying Liquid Glass to custom views
- Landmarks: Building an app with Liquid Glass