How to customize the background of NavigationStack in SwiftUI in 2024?

Mark Moeykens
Oct 16, 2021

Learn in SwiftUI:

  • How the NavigationStack changed

  • How background colors affect the NavigationStack

  • How to set a custom background color for the NavigationStack

  • How scrolling a List affects the NavigationStack

  • How to customize the NavigationStack when scrolling

  • How to set a custom font for the NavigationStack

  • Completely replace the NavigationStack with a custom view using safeAreaInset

  • Bonus: Using ZStack for a NavigationStack background

How the NavigationView changed

Starting in iOS 15, the background materials for bars (navigation bar, tab bar, etc.) were removed "giving more visual clarity to your content" as stated in this WWDC 2021 video titled "What's new in UIKit".

How background colors affect the NavigationView

When you set a background color, you will notice it will go behind the NavigationView for large and inline nav bars.

How to set a custom background color for the NavigationStack

In SwiftUI, starting in iOS 15:
The background of a view, such as shape styles, will automatically expand into the safe areas it touches.

When I say "shape style", I'm talking about styles that conform to the ShapeStyle protocol, such as:

  • Colors

  • Materials (blur effects)

  • Gradients

  • Hierarchical styles (secondary, tertiary, quaternary)

There is a new initializer introduced with iOS 15 that allows backgrounds to expand into safe areas. You could manually set the safe area edge but by default, it is set to "all" edges.

Solid Colors


  1. That Rectangle with the background HAS to touch the safe area edge.

  2. The shape style (Color) is in a background modifier because the background accepts a "ShapeStyle" type.

  3. The background touching the safe area edge should be the full width of the NavigationStack. The Rectangle shape expands horizontally to match the width of the device/NavigationStack.

You can use a Divider instead of a Rectangle. Let's combine it with a material this time:

This makes it look more like how NavigationStack used to look.

Gradients are also a ShapeStyle type. 
A vertical gradient doesn't work too well using this method because only the top color will bleed into the safe area.
Instead, use a linear gradient with some angle besides a vertical one.


  1. The Rectangle has a height and so it will add some padding to the bottom of the NavigationStack.

  2. Because the Rectangle has height, I filled it with a clear color so you can see its background gradient.

How scrolling a List affects the NavigationStack

When you scroll up a List, the NavigationStack will automatically turn into the inline style with a material background:

But as soon as you try to customize the NavigationStack, you lose this feature.

For example:

When you scroll up, the NavigationStack does not collapse inline. And the rows do not scroll behind the NavigationView.

For this to work, the List has to touch the NavigationStack.

So how can we customize the NavigationStack AND keep this behavior while scrolling?

How to customize the NavigationStack when scrolling

You will need to use the UINavigationBarAppearance from UIKit to customize the NavigationStack and keep the behavior when scrolling.


With a UINavigationBarAppearance, you can customize all different aspects of the NavigationStack.

This code:

  1. Sets a material (blur effect)

  2. Sets a color

  3. Assigns it to the scrollEdgeAppearance - Which you want to use when you have a scrollable view (like a List or ScrollView) touching the NavigationView.

Note: You can set these appearance properties in onAppear, the views init, or even in your App delegate when your app starts. It's up to you.

Here is the result:

Do you see the problem?
When the NavigationStack switched to inline (second screenshot), it lost the customizations!

The UINavigationBarAppearance allows you to customize different states or appearances of the NavigationStack.

So you need to apply the appearance you want to the standardAppearance of the NavigationStack:

Now it looks correct when scrolling up:

How to set a custom font for the NavigationStack

You will need the UINavigationBarAppearance again.

Notice you can set different fonts for large and inline titles:

Completely replace the NavigationStack with a custom view using safeAreaInset

If you don't want to go into UIKit land and use UINavigationBarAppearance then you can create your own navigation view.

Using safeAreaInset, you can easily add a view to a scrolling view (List, ScrollView) and have the list scroll behind your custom navigation view.


Don't forget to hide the NavigationStack (.navigationBarHidden(true)) when you add your own.


You learned you can customize the NavigationStack in SwiftUI with the use of ShapeStyles that are set as backgrounds to views touching the safe area edge of the NavigationStack.

You also learned how to use UINavigationBarAppearance for customizations.

Finally, you learned you can hide the NavigationStack and create your own with the use of the safeAreaInset modifier on a scrolling view.

SwiftUI Views Quick Start Book

Do you need a SwiftUI visual reference guide?

You can download the SwiftUI Views Quick Start Book for FREE to get you started.

Bonus: Using ZStack for a NavigationStack background

Someone suggested using a ZStack and a separate shape style to create a background.

This is totally doable.

Here's how I did it:


  • You need to ignore the top safe area edge.

  • The frame height is ZERO in this example. Greater than zero will make it grow downward from the NavigationStack. Pretty interesting.

Your Learning Path Quick Links

  1. Create UI with SwiftUI Views Mastery (beginner)

  2. Architect your app using Working with Data in SwiftUI (beginner)

  3. Improve your app's UX with SwiftUI Animations Mastery (junior)

  4. Save & sync your app's data with Core Data Mastery -or- SwiftData Mastery (junior) 

  5. React to and manipulate data using Combine Mastery in SwiftUI (advanced)


Chris Parker
Dec 1, 2021
These are awesome Mark.  
Mark Moeykens
Dec 1, 2021
Thanks, Chris!