diff --git a/RevenueCat.xcodeproj/project.pbxproj b/RevenueCat.xcodeproj/project.pbxproj
index 9ed02e0f75..c7098e44ca 100644
--- a/RevenueCat.xcodeproj/project.pbxproj
+++ b/RevenueCat.xcodeproj/project.pbxproj
@@ -12,6 +12,11 @@
 		030F918A2D55C1D20085103F /* LocaleFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F91892D55C1AB0085103F /* LocaleFinder.swift */; };
 		030F918C2D55C9DC0085103F /* LocaleFinderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F918B2D55C9D80085103F /* LocaleFinderTests.swift */; };
 		030F918E2D5664410085103F /* LocaleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F918D2D5664410085103F /* LocaleExtensions.swift */; };
+		030F91E32D56C4FD0085103F /* PaywallNavigationBarComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F91E22D56C4FD0085103F /* PaywallNavigationBarComponent.swift */; };
+		030F91E62D56F0850085103F /* NavigationBarComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F91E52D56F07F0085103F /* NavigationBarComponentViewModel.swift */; };
+		030F91FA2D57F1E70085103F /* NavigationViewIfNeeded.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F91F92D57F1E20085103F /* NavigationViewIfNeeded.swift */; };
+		030F91FC2D5945C10085103F /* NavigationBarPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F91FB2D5945AF0085103F /* NavigationBarPreview.swift */; };
+		030F920D2D595FE60085103F /* NavigationBarModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F920C2D595FDF0085103F /* NavigationBarModifier.swift */; };
 		0313FD41268A506400168386 /* DateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0313FD40268A506400168386 /* DateProvider.swift */; };
 		0354AA462D4029C300F9E330 /* TabControlButtonComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0354AA3E2D4029C300F9E330 /* TabControlButtonComponentViewModel.swift */; };
 		0354AA472D4029C300F9E330 /* TabControlComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0354AA402D4029C300F9E330 /* TabControlComponentViewModel.swift */; };
@@ -804,7 +809,6 @@
 		777FB4882C661C0600CD4749 /* SemanticVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 777FB4872C661C0600CD4749 /* SemanticVersionTests.swift */; };
 		7783606D2CCA7E14000785B8 /* PaywallStickyFooterComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7783606C2CCA7E14000785B8 /* PaywallStickyFooterComponent.swift */; };
 		778360792CCA85E4000785B8 /* StickyFooterComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 778360782CCA85E4000785B8 /* StickyFooterComponentViewModel.swift */; };
-		7783607B2CCA88E4000785B8 /* StickyFooterComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7783607A2CCA88E4000785B8 /* StickyFooterComponentView.swift */; };
 		77BA1AB12CCBAB80009BF0EA /* RootViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77BA1AB02CCBAB80009BF0EA /* RootViewModel.swift */; };
 		77BA1AB32CCBB6EE009BF0EA /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77BA1AB22CCBB6EE009BF0EA /* RootView.swift */; };
 		805B60C97993B311CEC93EAF /* ProductsFetcherSK2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3628C1F100BB3C1782860D24 /* ProductsFetcherSK2.swift */; };
@@ -1289,6 +1293,11 @@
 		030F91892D55C1AB0085103F /* LocaleFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocaleFinder.swift; sourceTree = "<group>"; };
 		030F918B2D55C9D80085103F /* LocaleFinderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocaleFinderTests.swift; sourceTree = "<group>"; };
 		030F918D2D5664410085103F /* LocaleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocaleExtensions.swift; sourceTree = "<group>"; };
+		030F91E22D56C4FD0085103F /* PaywallNavigationBarComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallNavigationBarComponent.swift; sourceTree = "<group>"; };
+		030F91E52D56F07F0085103F /* NavigationBarComponentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarComponentViewModel.swift; sourceTree = "<group>"; };
+		030F91F92D57F1E20085103F /* NavigationViewIfNeeded.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewIfNeeded.swift; sourceTree = "<group>"; };
+		030F91FB2D5945AF0085103F /* NavigationBarPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarPreview.swift; sourceTree = "<group>"; };
+		030F920C2D595FDF0085103F /* NavigationBarModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarModifier.swift; sourceTree = "<group>"; };
 		0313FD40268A506400168386 /* DateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateProvider.swift; sourceTree = "<group>"; };
 		0354AA3D2D4029C300F9E330 /* TabControlButtonComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabControlButtonComponentView.swift; sourceTree = "<group>"; };
 		0354AA3E2D4029C300F9E330 /* TabControlButtonComponentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabControlButtonComponentViewModel.swift; sourceTree = "<group>"; };
@@ -2101,7 +2110,6 @@
 		777FB4872C661C0600CD4749 /* SemanticVersionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SemanticVersionTests.swift; sourceTree = "<group>"; };
 		7783606C2CCA7E14000785B8 /* PaywallStickyFooterComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallStickyFooterComponent.swift; sourceTree = "<group>"; };
 		778360782CCA85E4000785B8 /* StickyFooterComponentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyFooterComponentViewModel.swift; sourceTree = "<group>"; };
-		7783607A2CCA88E4000785B8 /* StickyFooterComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyFooterComponentView.swift; sourceTree = "<group>"; };
 		77BA1AB02CCBAB80009BF0EA /* RootViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewModel.swift; sourceTree = "<group>"; };
 		77BA1AB22CCBB6EE009BF0EA /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
 		80E80EF026970DC3008F245A /* ReceiptFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptFetcher.swift; sourceTree = "<group>"; };
@@ -2563,6 +2571,14 @@
 			path = Localizations;
 			sourceTree = "<group>";
 		};
+		030F91E42D56F0750085103F /* NavigationBar */ = {
+			isa = PBXGroup;
+			children = (
+				030F91E52D56F07F0085103F /* NavigationBarComponentViewModel.swift */,
+			);
+			path = NavigationBar;
+			sourceTree = "<group>";
+		};
 		0354AA452D4029C300F9E330 /* Tabs */ = {
 			isa = PBXGroup;
 			children = (
@@ -2637,6 +2653,7 @@
 		2C2AEB0D2CA64DA900A50F38 /* TemplateComponentsViewPreviews */ = {
 			isa = PBXGroup;
 			children = (
+				030F91FB2D5945AF0085103F /* NavigationBarPreview.swift */,
 				03A98CF02D222F53009BCA61 /* FallbackComponentPreview.swift */,
 				2C2AEB0E2CA64E0E00A50F38 /* Template1Preview.swift */,
 				2C8EC6DA2CCC23B700D6CCF8 /* MultiTierPreview.swift */,
@@ -2666,6 +2683,7 @@
 				0354AA452D4029C300F9E330 /* Tabs */,
 				4D3BA5B12D47AB4400668AFC /* Timeline */,
 				88B1BADC2C813A3C001B7EE5 /* Text */,
+				030F91E42D56F0750085103F /* NavigationBar */,
 				778360772CCA85D1000785B8 /* StickyFooter */,
 				778360742CCA84FA000785B8 /* Root */,
 			);
@@ -2675,6 +2693,8 @@
 		2C7457442CEA652B004ACE52 /* ViewHelpers */ = {
 			isa = PBXGroup;
 			children = (
+				030F920C2D595FDF0085103F /* NavigationBarModifier.swift */,
+				030F91F92D57F1E20085103F /* NavigationViewIfNeeded.swift */,
 				2C7457872CEDF7AC004ACE52 /* BackgroundStyle.swift */,
 				77089F9D2CD39EC100848CD5 /* ShadowModifier.swift */,
 				4D6F4BCF2CF69DE300353AF6 /* ForegroundColorScheme.swift */,
@@ -4585,7 +4605,6 @@
 			isa = PBXGroup;
 			children = (
 				778360782CCA85E4000785B8 /* StickyFooterComponentViewModel.swift */,
-				7783607A2CCA88E4000785B8 /* StickyFooterComponentView.swift */,
 			);
 			path = StickyFooter;
 			sourceTree = "<group>";
@@ -4963,6 +4982,7 @@
 				2C2AEB3A2CA7209F00A50F38 /* PaywallPackageComponent.swift */,
 				2C2AEB3E2CA7235300A50F38 /* PaywallPurchaseButtonComponent.swift */,
 				7783606C2CCA7E14000785B8 /* PaywallStickyFooterComponent.swift */,
+				030F91E22D56C4FD0085103F /* PaywallNavigationBarComponent.swift */,
 			);
 			path = Components;
 			sourceTree = "<group>";
@@ -6199,6 +6219,7 @@
 				5736267B2D3E76C1003C9665 /* ProductPaidPrice.swift in Sources */,
 				5712BE9029241EB500A83F15 /* TimingUtil.swift in Sources */,
 				B3B5FBC1269E17CE00104A0C /* DeviceCache.swift in Sources */,
+				030F91E32D56C4FD0085103F /* PaywallNavigationBarComponent.swift in Sources */,
 				F5BE424226965F9F00254A30 /* ProductRequestData+Initialization.swift in Sources */,
 				2DDF41AD24F6F37C005BC22D /* ASN1ObjectIdentifier.swift in Sources */,
 				35549323269E298B005F9AE9 /* OfferingsFactory.swift in Sources */,
@@ -6759,6 +6780,7 @@
 				887A607F2C1D037000E1A461 /* Optional+Extensions.swift in Sources */,
 				2C08B3172CDA44440024857B /* ViewModelFactory.swift in Sources */,
 				356979E02CCFDAA100EE6A9E /* CustomerInfoFixtures.swift in Sources */,
+				030F91FC2D5945C10085103F /* NavigationBarPreview.swift in Sources */,
 				3511088F2C47F6DA0048C4D8 /* CustomerInfo+CurrentEntitlement.swift in Sources */,
 				887A60752C1D037000E1A461 /* TemplateViewConfiguration.swift in Sources */,
 				03C72FC32D349BAE00297FEC /* IconComponentView.swift in Sources */,
@@ -6779,6 +6801,7 @@
 				574BA26B2D3E762E009B8EA6 /* PurchaseInfo.swift in Sources */,
 				887A60C92C1D037000E1A461 /* PurchaseButton.swift in Sources */,
 				88B1BAF02C813A3C001B7EE5 /* TextComponentViewModel.swift in Sources */,
+				030F91E62D56F0850085103F /* NavigationBarComponentViewModel.swift in Sources */,
 				2D2AFE912C6A9EF500D1B0B4 /* Binding+Extensions.swift in Sources */,
 				2C7457242CE713E5004ACE52 /* PreviewMock.swift in Sources */,
 				7706ED3E2C6E374D0004B9F9 /* ButtonStyles.swift in Sources */,
@@ -6797,6 +6820,7 @@
 				03A98CF12D222F5F009BCA61 /* FallbackComponentPreview.swift in Sources */,
 				887A60862C1D037000E1A461 /* FooterHidingModifier.swift in Sources */,
 				4D3BA5882D432E5900668AFC /* Fill.swift in Sources */,
+				030F920D2D595FE60085103F /* NavigationBarModifier.swift in Sources */,
 				FDAD6AC72D132DD900FB047E /* CustomerCenterStoreKitUtilitiesType.swift in Sources */,
 				887A60C02C1D037000E1A461 /* AsyncButton.swift in Sources */,
 				887A60892C1D037000E1A461 /* PaywallPurchasesType.swift in Sources */,
@@ -6839,7 +6863,6 @@
 				2C4C36132C6FBA8B00AE959B /* CompatibilityTopBarTrailing.swift in Sources */,
 				3546355C2C391F38001D7E85 /* FeedbackSurveyViewModel.swift in Sources */,
 				353756692C382C2800A1B8D6 /* CustomerCenterViewState.swift in Sources */,
-				7783607B2CCA88E4000785B8 /* StickyFooterComponentView.swift in Sources */,
 				887A608B2C1D037000E1A461 /* PurchaseHandler+TestData.swift in Sources */,
 				35F249CE2C493E3D0058993A /* CustomerCenterPurchases.swift in Sources */,
 				353756672C382C2800A1B8D6 /* PurchaseInformation.swift in Sources */,
@@ -6877,6 +6900,7 @@
 				887A60CE2C1D037000E1A461 /* View+PresentPaywall.swift in Sources */,
 				357CEC702C5940CE00A80837 /* ColorFromAppearance.swift in Sources */,
 				887A60832C1D037000E1A461 /* VersionDetector.swift in Sources */,
+				030F91FA2D57F1E70085103F /* NavigationViewIfNeeded.swift in Sources */,
 				574D1C702D3E75F9005840CD /* PurchaseDetailView.swift in Sources */,
 				574D1C712D3E75F9005840CD /* PurchaseHistoryView.swift in Sources */,
 				574D1C722D3E75F9005840CD /* PurchaseLinkView.swift in Sources */,
diff --git a/RevenueCatUI/Templates/V2/Components/ComponentsView.swift b/RevenueCatUI/Templates/V2/Components/ComponentsView.swift
index 44bac1453d..e5aa9e296a 100644
--- a/RevenueCatUI/Templates/V2/Components/ComponentsView.swift
+++ b/RevenueCatUI/Templates/V2/Components/ComponentsView.swift
@@ -62,8 +62,6 @@ struct ComponentsView: View {
                     PackageComponentView(viewModel: viewModel, onDismiss: onDismiss)
                 case .purchaseButton(let viewModel):
                     PurchaseButtonComponentView(viewModel: viewModel)
-                case .stickyFooter(let viewModel):
-                    StickyFooterComponentView(viewModel: viewModel)
                 case .timeline(let viewModel):
                     TimelineComponentView(viewModel: viewModel)
                 case .tabs(let viewModel):
diff --git a/RevenueCatUI/Templates/V2/Components/NavigationBar/NavigationBarComponentViewModel.swift b/RevenueCatUI/Templates/V2/Components/NavigationBar/NavigationBarComponentViewModel.swift
new file mode 100644
index 0000000000..5f6d610bcb
--- /dev/null
+++ b/RevenueCatUI/Templates/V2/Components/NavigationBar/NavigationBarComponentViewModel.swift
@@ -0,0 +1,39 @@
+//
+//  Copyright RevenueCat Inc. All Rights Reserved.
+//
+//  Licensed under the MIT License (the "License");
+//  you may not use this file except in compliance with the License.
+//  You may obtain a copy of the License at
+//
+//      https://opensource.org/licenses/MIT
+//
+//  NavigationBarComponentViewModel.swift
+//
+//  Created by Josh Holtz on 2/7/25.
+
+import RevenueCat
+import SwiftUI
+
+#if !os(macOS) && !os(tvOS) // For Paywalls V2
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+class NavigationBarComponentViewModel {
+
+    let component: PaywallComponent.NavigationBarComponent
+
+    let leadingStackViewModel: StackComponentViewModel?
+    let trailingStackViewModel: StackComponentViewModel?
+
+    init(
+        component: PaywallComponent.NavigationBarComponent,
+        leadingStackViewModel: StackComponentViewModel?,
+        trailingStackViewModel: StackComponentViewModel?
+    ) {
+        self.component = component
+        self.leadingStackViewModel = leadingStackViewModel
+        self.trailingStackViewModel = trailingStackViewModel
+    }
+
+}
+
+#endif
diff --git a/RevenueCatUI/Templates/V2/Components/Root/RootViewModel.swift b/RevenueCatUI/Templates/V2/Components/Root/RootViewModel.swift
index fd301103e1..99a6548837 100644
--- a/RevenueCatUI/Templates/V2/Components/Root/RootViewModel.swift
+++ b/RevenueCatUI/Templates/V2/Components/Root/RootViewModel.swift
@@ -25,15 +25,18 @@ class RootViewModel {
     }
 
     let stackViewModel: StackComponentViewModel
+    let navigationBarViewModel: NavigationBarComponentViewModel?
     let stickyFooterViewModel: StickyFooterComponentViewModel?
     let firstImageInfo: FirstImageInfo?
 
     init(
         stackViewModel: StackComponentViewModel,
+        navigationBarViewModel: NavigationBarComponentViewModel?,
         stickyFooterViewModel: StickyFooterComponentViewModel?,
         firstImageInfo: FirstImageInfo?
     ) {
         self.stackViewModel = stackViewModel
+        self.navigationBarViewModel = navigationBarViewModel
         self.stickyFooterViewModel = stickyFooterViewModel
         self.firstImageInfo = firstImageInfo
     }
diff --git a/RevenueCatUI/Templates/V2/Components/StickyFooter/StickyFooterComponentView.swift b/RevenueCatUI/Templates/V2/Components/StickyFooter/StickyFooterComponentView.swift
deleted file mode 100644
index 54a072c981..0000000000
--- a/RevenueCatUI/Templates/V2/Components/StickyFooter/StickyFooterComponentView.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-//  Copyright RevenueCat Inc. All Rights Reserved.
-//
-//  Licensed under the MIT License (the "License");
-//  you may not use this file except in compliance with the License.
-//  You may obtain a copy of the License at
-//
-//      https://opensource.org/licenses/MIT
-//
-//  StickyFooterComponentView.swift
-//
-//  Created by Jay Shortway on 24/10/2024.
-
-import Foundation
-import RevenueCat
-import SwiftUI
-
-#if !os(macOS) && !os(tvOS) // For Paywalls V2
-
-@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
-struct StickyFooterComponentView: View {
-    private let viewModel: StickyFooterComponentViewModel
-
-    internal init(viewModel: StickyFooterComponentViewModel) {
-        self.viewModel = viewModel
-    }
-
-    var body: some View {
-        EmptyView()
-    }
-
-}
-
-#endif
diff --git a/RevenueCatUI/Templates/V2/PaywallsV2View.swift b/RevenueCatUI/Templates/V2/PaywallsV2View.swift
index a5ea77b263..0722b38d93 100644
--- a/RevenueCatUI/Templates/V2/PaywallsV2View.swift
+++ b/RevenueCatUI/Templates/V2/PaywallsV2View.swift
@@ -170,6 +170,8 @@ struct PaywallsV2View: View {
                 .task {
                     await self.introOfferEligibilityContext.computeEligibility(for: paywallState.packages)
                 }
+                // Needs to be last since this can wrap the view in a NavigationView/NavigationStack if needed
+                .navigationBarIfNeeded(paywallState.rootViewModel.navigationBarViewModel, onDismiss: self.onDismiss)
             case .failure(let error):
                 // Show fallback paywall and debug error message that
                 // occurred while validating data and view models
diff --git a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/FamilySharingTogglePreview.swift b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/FamilySharingTogglePreview.swift
index edb85fe15c..44944e3989 100644
--- a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/FamilySharingTogglePreview.swift
+++ b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/FamilySharingTogglePreview.swift
@@ -333,6 +333,7 @@ private enum FamilySharingTogglePreview {
                         .stack(stack)
                     ]
                 ),
+                navigationBar: nil,
                 stickyFooter: nil,
                 background: .color(.init(light: .hex("#ffffff")))
             )
diff --git a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/MultiTierPreview.swift b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/MultiTierPreview.swift
index 24d533cafc..2143ab30d0 100644
--- a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/MultiTierPreview.swift
+++ b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/MultiTierPreview.swift
@@ -342,6 +342,7 @@ private enum MultiTierPreview {
                         .stack(stack)
                     ]
                 ),
+                navigationBar: nil,
                 stickyFooter: nil,
                 background: .color(.init(light: .hex("#ffffff")))
             )
diff --git a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/NavigationBarPreview.swift b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/NavigationBarPreview.swift
new file mode 100644
index 0000000000..35c2dcbdbd
--- /dev/null
+++ b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/NavigationBarPreview.swift
@@ -0,0 +1,293 @@
+//
+//  Copyright RevenueCat Inc. All Rights Reserved.
+//
+//  Licensed under the MIT License (the "License");
+//  you may not use this file except in compliance with the License.
+//  You may obtain a copy of the License at
+//
+//      https://opensource.org/licenses/MIT
+//
+//  NavigationBarPreview.swift
+//
+//  Created by Josh Holtz on 2/9/25.
+
+import Foundation
+import RevenueCat
+import SwiftUI
+
+#if !os(macOS) && !os(tvOS) // For Paywalls V2
+
+#if DEBUG
+
+private enum NavigationBarPreview {
+
+    static let catUrl = URL(string: "https://assets.pawwalls.com/954459_1701163461.jpg")!
+
+    static let catImage = PaywallComponent.ImageComponent(
+        source: .init(
+            light: .init(
+                width: 750,
+                height: 530,
+                original: catUrl,
+                heic: catUrl,
+                heicLowRes: catUrl
+            )
+        ),
+        size: .init(width: .fill, height: .fixed(270)),
+        fitMode: .fill,
+        maskShape: .convex
+    )
+
+    static let title = PaywallComponent.TextComponent(
+        text: "title",
+        fontName: nil,
+        fontWeight: .black,
+        color: .init(light: .hex("#000000")),
+        backgroundColor: nil,
+        padding: .zero,
+        margin: .zero,
+        fontSize: 28,
+        horizontalAlignment: .center
+    )
+
+    static let body = PaywallComponent.TextComponent(
+        text: "body",
+        fontName: nil,
+        fontWeight: .regular,
+        color: .init(light: .hex("#000000")),
+        backgroundColor: nil,
+        padding: .zero,
+        margin: .zero,
+        fontSize: 15,
+        horizontalAlignment: .center
+    )
+
+    static var packageStack: PaywallComponent.StackComponent {
+        return .init(
+            components: [
+                .text(.init(
+                    text: "package_name",
+                    fontWeight: .bold,
+                    color: .init(light: .hex("#000000")),
+                    padding: .zero,
+                    margin: .zero
+                )),
+                .text(.init(
+                    text: "package_detail",
+                    color: .init(light: .hex("#000000")),
+                    padding: .zero,
+                    margin: .zero
+                ))
+            ],
+            dimension: .vertical(.center, .start),
+            spacing: 0,
+            backgroundColor: nil,
+            padding: .init(top: 0,
+                           bottom: 0,
+                           leading: 0,
+                           trailing: 0)
+        )
+    }
+
+    static let package = PaywallComponent.PackageComponent(
+        packageID: "weekly",
+        isSelectedByDefault: false,
+        stack: packageStack
+    )
+
+    static let purchaseButton = PaywallComponent.PurchaseButtonComponent(
+        stack: .init(
+            components: [
+                // WIP: Intro offer state with "cta_intro",
+                .text(.init(
+                    text: "cta",
+                    fontWeight: .bold,
+                    color: .init(light: .hex("#ffffff")),
+                    backgroundColor: .init(light: .hex("#e89d89")),
+                    padding: .init(top: 10,
+                                   bottom: 10,
+                                   leading: 30,
+                                   trailing: 30)
+                ))
+            ],
+            shape: .pill
+        )
+    )
+
+    static let contentStack = PaywallComponent.StackComponent(
+        components: [
+            .text(title),
+            .text(body),
+            .package(package),
+            .purchaseButton(purchaseButton)
+        ],
+        spacing: 30,
+        backgroundColor: nil,
+        margin: .init(top: 0,
+                      bottom: 0,
+                      leading: 20,
+                      trailing: 20)
+    )
+
+    static let stack = PaywallComponent.StackComponent(
+        components: [
+            .image(catImage),
+            .stack(contentStack)
+        ],
+        spacing: 20,
+        backgroundColor: nil
+    )
+
+    static let paywallComponents: Offering.PaywallComponents = .init(
+        uiConfig: .init(
+            app: .init(
+                colors: [:],
+                fonts: [:]
+            ),
+            localizations: [:],
+            variableConfig: .init(
+                variableCompatibilityMap: [:],
+                functionCompatibilityMap: [:]
+            )
+        ),
+        data: data
+    )
+
+    static let data: PaywallComponentsData = .init(
+        templateName: "components",
+        assetBaseURL: URL(string: "https://assets.pawwalls.com")!,
+        componentsConfig: .init(
+            base: .init(
+                stack: .init(
+                    components: [
+                        .stack(stack)
+                    ]
+                ),
+                navigationBar: .init(
+                    trailingStack: .init(
+                        components: [
+                            .button(.init(
+                                action: .navigateBack,
+                                stack: .init(
+                                    components: [
+                                        .text(
+                                            .init(
+                                                text: "nav_close",
+                                                color: .init(light: .hex("#000000"))
+                                            )
+                                        )
+                                    ]
+                                )
+                            ))
+                        ]
+                    )
+                ),
+                stickyFooter: nil,
+                background: .color(.init(
+                    light: .hex("#ffffff")
+                ))
+            )
+        ),
+        componentsLocalizations: ["en_US": [
+            "title": .string("Ignite your cat's curiosity"),
+            "body": .string("Get access to all of our educational content trusted by thousands of pet parents."),
+            "package_name": .string("Monthly"),
+            "package_detail": .string("Some price into"),
+            "cta": .string("Get Started"),
+            "cta_intro": .string("Claim Free Trial"),
+            "nav_close": .string("Close")
+        ]],
+        revision: 1,
+        defaultLocaleIdentifier: "en_US"
+    )
+}
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+struct NavigationBarPreview_Previews: PreviewProvider {
+
+    static var package: Package {
+        return .init(identifier: "weekly",
+                     packageType: .weekly,
+                     storeProduct: .init(sk1Product: .init()),
+                     offeringIdentifier: "default")
+    }
+
+    // Need to wrap in VStack otherwise preview rerenders and images won't show
+    static var previews: some View {
+        ExampleView(package: self.package)
+        .previewRequiredEnvironmentProperties()
+        .previewLayout(.fixed(width: 400, height: 800))
+        .previewDisplayName("Template 1")
+    }
+}
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+private struct ExampleView: View {
+
+    let package: Package
+
+    @State
+    private var showSheet = false
+
+    @State
+    private var showFullCover = false
+
+    var body: some View {
+        NavigationView {
+            VStack(spacing: 20) {
+
+                NavigationLink(destination: ThePaywallView(package: self.package)) {
+                    Text("Push")
+                }
+
+                Button(action: {
+                    self.showSheet = true
+                }, label: {
+                    Text("Sheet")
+                })
+
+                Button(action: {
+                    self.showFullCover = true
+                }, label: {
+                    Text("Full Screen")
+                })
+
+            }
+        }
+        .sheet(isPresented: self.$showSheet) {
+            ThePaywallView(package: self.package)
+        }
+        .fullScreenCover(isPresented: self.$showFullCover) {
+            ThePaywallView(package: self.package)
+        }
+    }
+
+}
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+private struct ThePaywallView: View {
+
+    @Environment(\.dismiss) private var dismiss
+
+    let package: Package
+
+    var body: some View {
+        PaywallsV2View(
+            paywallComponents: NavigationBarPreview.paywallComponents,
+            offering: .init(identifier: "default",
+                            serverDescription: "",
+                            availablePackages: [self.package]),
+            introEligibilityChecker: .default(),
+            showZeroDecimalPlacePrices: true,
+            onDismiss: {
+                self.dismiss()
+            },
+            fallbackContent: .customView(AnyView(Text("Fallback paywall")))
+        )
+    }
+
+}
+
+#endif
+
+#endif
diff --git a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/Template1Preview.swift b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/Template1Preview.swift
index 8f38ad8614..23496312b7 100644
--- a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/Template1Preview.swift
+++ b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/Template1Preview.swift
@@ -163,6 +163,7 @@ private enum Template1Preview {
                         .stack(stack)
                     ]
                 ),
+                navigationBar: nil,
                 stickyFooter: nil,
                 background: .color(.init(
                     light: .hex("#ffffff")
diff --git a/RevenueCatUI/Templates/V2/ViewHelpers/NavigationBarModifier.swift b/RevenueCatUI/Templates/V2/ViewHelpers/NavigationBarModifier.swift
new file mode 100644
index 0000000000..ed42d9a1ee
--- /dev/null
+++ b/RevenueCatUI/Templates/V2/ViewHelpers/NavigationBarModifier.swift
@@ -0,0 +1,74 @@
+//
+//  Copyright RevenueCat Inc. All Rights Reserved.
+//
+//  Licensed under the MIT License (the "License");
+//  you may not use this file except in compliance with the License.
+//  You may obtain a copy of the License at
+//
+//      https://opensource.org/licenses/MIT
+//
+//  NavigationBarModifier.swift
+//
+//  Created by Josh Holtz on 2/9/25.
+
+import Foundation
+import SwiftUI
+
+#if !os(macOS) && !os(tvOS) // For Paywalls V2
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+struct NavigationBarIfNeededModifier: ViewModifier {
+
+    let viewModel: NavigationBarComponentViewModel?
+    let onDismiss: () -> Void
+
+    var needsToolbar: Bool {
+        return self.viewModel?.leadingStackViewModel != nil || self.viewModel?.trailingStackViewModel != nil
+    }
+
+    func body(content: Content) -> some View {
+        if needsToolbar {
+            NavigationViewIfNeeded {
+                content
+                    .applyIfLet(self.viewModel?.leadingStackViewModel) { view, stack in
+                        view.toolbar(content: {
+                            ToolbarItem(placement: .topBarLeading) {
+                                ComponentsView(
+                                    componentViewModels: [.stack(stack)],
+                                    onDismiss: self.onDismiss
+                                )
+                            }
+                        })
+                    }
+                    .applyIfLet(self.viewModel?.trailingStackViewModel) { view, stack in
+                        view.toolbar(content: {
+                            ToolbarItem(placement: .topBarTrailing) {
+                                ComponentsView(
+                                    componentViewModels: [.stack(stack)],
+                                    onDismiss: self.onDismiss
+                                )
+                            }
+                        })
+                    }
+            }
+        } else {
+            content
+        }
+    }
+
+}
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+extension View {
+    func navigationBarIfNeeded(
+        _ viewModel: NavigationBarComponentViewModel?,
+        onDismiss: @escaping () -> Void
+    ) -> some View {
+        self.modifier(NavigationBarIfNeededModifier(
+            viewModel: viewModel,
+            onDismiss: onDismiss
+        ))
+    }
+}
+
+#endif
diff --git a/RevenueCatUI/Templates/V2/ViewHelpers/NavigationViewIfNeeded.swift b/RevenueCatUI/Templates/V2/ViewHelpers/NavigationViewIfNeeded.swift
new file mode 100644
index 0000000000..bc8d719324
--- /dev/null
+++ b/RevenueCatUI/Templates/V2/ViewHelpers/NavigationViewIfNeeded.swift
@@ -0,0 +1,100 @@
+//
+//  Copyright RevenueCat Inc. All Rights Reserved.
+//
+//  Licensed under the MIT License (the "License");
+//  you may not use this file except in compliance with the License.
+//  You may obtain a copy of the License at
+//
+//      https://opensource.org/licenses/MIT
+//
+//  NavigationViewIfNeeded.swift
+//
+//  Created by Josh Holtz on 2/8/25.
+
+import SwiftUI
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+struct NavigationViewIfNeeded<Content: View>: View {
+    enum Status {
+        case unknown
+        case inNav
+        case notInNav
+    }
+
+    @State private var status: Status = .unknown
+
+    let content: Content
+
+    init(@ViewBuilder content: () -> Content) {
+        self.content = content()
+    }
+
+    var body: some View {
+        switch status {
+        case .unknown:
+            Rectangle()
+                .frame(width: 0, height: 0)
+                .toolbar {
+                    // Zero-sized detection view:
+                    ZeroFrameDetectionView { isInNav in
+                        // The first time we know the answer, store it
+                        if status == .unknown {
+                            status = isInNav ? .inNav : .notInNav
+                        }
+                    }
+                }
+        case .inNav:
+            content
+        case .notInNav:
+            #if swift(>=5.7)
+            if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
+                // Using NavigationStack is best generic solution if only need
+                // to show a toolbar
+                // NavigatonStack toolbars combine nicely in parent NavigationView
+                NavigationStack {
+                    content
+                }
+            } else {
+                NavigationView {
+                    content
+                }
+            }
+            #else
+            NavigationView {
+                content
+            }
+            #endif
+        }
+    }
+}
+
+// This minimal subview does the environment check and calls back exactly once.
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+private struct ZeroFrameDetectionView: View {
+    @Environment(\.isPresented) private var isPresented
+    let didDetect: (Bool) -> Void
+
+    @State private var hasReported = false
+
+    var body: some View {
+        Rectangle()
+            .frame(width: 0, height: 0)
+            .onChange(of: isPresented) { newValue in
+                if newValue {
+                    self.report(true)
+                }
+            }
+            .onAppear {
+                // Dispatch once after SwiftUI lay out
+                DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
+                    self.report(false)
+                }
+            }
+    }
+
+    private func report(_ value: Bool) {
+        guard !self.hasReported else { return }
+        self.hasReported = true
+        self.didDetect(value)
+    }
+}
diff --git a/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentViewModel.swift b/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentViewModel.swift
index 21508a6276..37bfcea4d0 100644
--- a/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentViewModel.swift
+++ b/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentViewModel.swift
@@ -28,7 +28,6 @@ enum PaywallComponentViewModel {
     case button(ButtonComponentViewModel)
     case package(PackageComponentViewModel)
     case purchaseButton(PurchaseButtonComponentViewModel)
-    case stickyFooter(StickyFooterComponentViewModel)
     case timeline(TimelineComponentViewModel)
 
     case tabs(TabsComponentViewModel)
diff --git a/RevenueCatUI/Templates/V2/ViewModelHelpers/ViewModelFactory.swift b/RevenueCatUI/Templates/V2/ViewModelHelpers/ViewModelFactory.swift
index 421e1148d3..e5d3e55b69 100644
--- a/RevenueCatUI/Templates/V2/ViewModelHelpers/ViewModelFactory.swift
+++ b/RevenueCatUI/Templates/V2/ViewModelHelpers/ViewModelFactory.swift
@@ -10,6 +10,7 @@
 //  ViewModelFactory.swift
 //
 //  Created by Josh Holtz on 11/5/24.
+// swiftlint:disable function_body_length type_body_length file_length
 
 import Foundation
 import RevenueCat
@@ -17,11 +18,11 @@ import RevenueCat
 #if !os(macOS) && !os(tvOS) // For Paywalls V2
 
 @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
-// swiftlint:disable:next type_body_length
 struct ViewModelFactory {
 
     let packageValidator = PackageValidator()
 
+    // swiftlint:disable:next function_body_length
     func toRootViewModel(
         componentsConfig: PaywallComponentsData.PaywallComponentsConfig,
         offering: Offering,
@@ -55,8 +56,39 @@ struct ViewModelFactory {
             )
         }
 
+        let navigationBarViewModel = try componentsConfig.navigationBar.flatMap {
+            let leadingStackViewModel = try $0.leadingStack.flatMap { stackComponent in
+                return try toStackViewModel(
+                    component: stackComponent,
+                    packageValidator: self.packageValidator,
+                    firstImageInfo: nil,
+                    localizationProvider: localizationProvider,
+                    uiConfigProvider: uiConfigProvider,
+                    offering: offering
+                )
+            }
+
+            let trailingStackViewModel = try $0.trailingStack.flatMap { stackComponent in
+                return try toStackViewModel(
+                    component: stackComponent,
+                    packageValidator: self.packageValidator,
+                    firstImageInfo: nil,
+                    localizationProvider: localizationProvider,
+                    uiConfigProvider: uiConfigProvider,
+                    offering: offering
+                )
+            }
+
+            return NavigationBarComponentViewModel(
+                component: $0,
+                leadingStackViewModel: leadingStackViewModel,
+                trailingStackViewModel: trailingStackViewModel
+            )
+        }
+
         return RootViewModel(
             stackViewModel: rootStackViewModel,
+            navigationBarViewModel: navigationBarViewModel,
             stickyFooterViewModel: stickyFooterViewModel,
             firstImageInfo: firstImageInfo
         )
@@ -159,22 +191,6 @@ struct ViewModelFactory {
             return .purchaseButton(
                 PurchaseButtonComponentViewModel(stackViewModel: stackViewModel)
             )
-        case .stickyFooter(let component):
-            let stackViewModel = try toStackViewModel(
-                component: component.stack,
-                packageValidator: packageValidator,
-                firstImageInfo: firstImageInfo,
-                localizationProvider: localizationProvider,
-                uiConfigProvider: uiConfigProvider,
-                offering: offering
-            )
-
-            return .stickyFooter(
-                StickyFooterComponentViewModel(
-                    component: component,
-                    stackViewModel: stackViewModel
-                )
-            )
         case .timeline(let component):
             let models = try component.items.map { item in
                 var description: TextComponentViewModel?
@@ -331,7 +347,7 @@ struct ViewModelFactory {
         )
     }
 
-    // swiftlint:disable cyclomatic_complexity function_body_length
+    // swiftlint:disable cyclomatic_complexity
     private func findFullWidthImageViewIfItsTheFirst(
         _ component: PaywallComponent
     ) -> RootViewModel.FirstImageInfo? {
@@ -374,8 +390,6 @@ struct ViewModelFactory {
             return self.findFullWidthImageViewIfItsTheFirst(first)
         case .purchaseButton:
             return nil
-        case .stickyFooter:
-            return nil
         case .timeline:
             return nil
         case .tabs(let tabs):
diff --git a/Sources/Networking/Responses/RevenueCatUI/PaywallComponentsData.swift b/Sources/Networking/Responses/RevenueCatUI/PaywallComponentsData.swift
index 1621aab511..98c625f430 100644
--- a/Sources/Networking/Responses/RevenueCatUI/PaywallComponentsData.swift
+++ b/Sources/Networking/Responses/RevenueCatUI/PaywallComponentsData.swift
@@ -28,16 +28,19 @@ public struct PaywallComponentsData: Codable, Equatable, Sendable {
 
     public struct PaywallComponentsConfig: Codable, Equatable, Sendable {
 
-        public var stack: PaywallComponent.StackComponent
+        public let stack: PaywallComponent.StackComponent
+        public let navigationBar: PaywallComponent.NavigationBarComponent?
         public let stickyFooter: PaywallComponent.StickyFooterComponent?
-        public var background: PaywallComponent.Background
+        public let background: PaywallComponent.Background
 
         public init(
             stack: PaywallComponent.StackComponent,
+            navigationBar: PaywallComponent.NavigationBarComponent?,
             stickyFooter: PaywallComponent.StickyFooterComponent?,
             background: PaywallComponent.Background
         ) {
             self.stack = stack
+            self.navigationBar = navigationBar
             self.stickyFooter = stickyFooter
             self.background = background
         }
@@ -144,6 +147,7 @@ extension PaywallComponentsData {
             errors["componentsConfig"] = .init(error)
             componentsConfig = ComponentsConfig(base: PaywallComponentsConfig(
                 stack: .init(components: []),
+                navigationBar: nil,
                 stickyFooter: nil,
                 background: .color(.init(light: .hex("#ffffff")))
             ))
diff --git a/Sources/Paywalls/Components/Common/PaywallComponentBase.swift b/Sources/Paywalls/Components/Common/PaywallComponentBase.swift
index 941820ec78..2bebb49087 100644
--- a/Sources/Paywalls/Components/Common/PaywallComponentBase.swift
+++ b/Sources/Paywalls/Components/Common/PaywallComponentBase.swift
@@ -18,7 +18,6 @@ public enum PaywallComponent: PaywallComponentBase {
     case button(ButtonComponent)
     case package(PackageComponent)
     case purchaseButton(PurchaseButtonComponent)
-    case stickyFooter(StickyFooterComponent)
     case timeline(TimelineComponent)
 
     case tabs(TabsComponent)
@@ -35,7 +34,6 @@ public enum PaywallComponent: PaywallComponentBase {
         case button
         case package
         case purchaseButton = "purchase_button"
-        case stickyFooter = "sticky_footer"
         case timeline
 
         case tabs
@@ -89,9 +87,6 @@ extension PaywallComponent: Codable {
         case .purchaseButton(let component):
             try container.encode(ComponentType.purchaseButton, forKey: .type)
             try component.encode(to: encoder)
-        case .stickyFooter(let component):
-            try container.encode(ComponentType.stickyFooter, forKey: .type)
-            try component.encode(to: encoder)
         case .timeline(let component):
             try container.encode(ComponentType.timeline, forKey: .type)
             try component.encode(to: encoder)
@@ -174,8 +169,6 @@ extension PaywallComponent: Codable {
             return .package(try PackageComponent(from: decoder))
         case .purchaseButton:
             return .purchaseButton(try PurchaseButtonComponent(from: decoder))
-        case .stickyFooter:
-            return .stickyFooter(try StickyFooterComponent(from: decoder))
         case .timeline:
             return .timeline(try TimelineComponent(from: decoder))
         case .tabs:
diff --git a/Sources/Paywalls/Components/PaywallNavigationBarComponent.swift b/Sources/Paywalls/Components/PaywallNavigationBarComponent.swift
new file mode 100644
index 0000000000..726e97e065
--- /dev/null
+++ b/Sources/Paywalls/Components/PaywallNavigationBarComponent.swift
@@ -0,0 +1,44 @@
+//
+//  Copyright RevenueCat Inc. All Rights Reserved.
+//
+//  Licensed under the MIT License (the "License");
+//  you may not use this file except in compliance with the License.
+//  You may obtain a copy of the License at
+//
+//      https://opensource.org/licenses/MIT
+//
+//  PaywallStickyFooterComponent.swift
+//
+//  Created by Jay Shortway on 24/10/2024.
+//
+// swiftlint:disable missing_docs
+
+import Foundation
+
+public extension PaywallComponent {
+
+    final class NavigationBarComponent: PaywallComponentBase {
+
+        public let leadingStack: PaywallComponent.StackComponent?
+        public let trailingStack: PaywallComponent.StackComponent?
+
+        public init(
+            leadingStack: PaywallComponent.StackComponent? = nil,
+            trailingStack: PaywallComponent.StackComponent? = nil
+        ) {
+            self.leadingStack = leadingStack
+            self.trailingStack = trailingStack
+        }
+
+        public func hash(into hasher: inout Hasher) {
+            hasher.combine(leadingStack)
+            hasher.combine(trailingStack)
+        }
+
+        public static func == (lhs: NavigationBarComponent, rhs: NavigationBarComponent) -> Bool {
+            return lhs.leadingStack == rhs.leadingStack &&
+                   lhs.trailingStack == rhs.trailingStack
+        }
+        }
+
+}
diff --git a/Sources/Paywalls/Components/PaywallV2CacheWarming.swift b/Sources/Paywalls/Components/PaywallV2CacheWarming.swift
index aeb15c767d..cefbe1b697 100644
--- a/Sources/Paywalls/Components/PaywallV2CacheWarming.swift
+++ b/Sources/Paywalls/Components/PaywallV2CacheWarming.swift
@@ -40,7 +40,10 @@ extension PaywallComponentsData.PaywallComponentsConfig {
         let rootStackImageURLs = self.collectAllImageURLs(in: self.stack)
         let stickFooterImageURLs = self.stickyFooter.flatMap { self.collectAllImageURLs(in: $0.stack) } ?? []
 
-        return rootStackImageURLs + stickFooterImageURLs
+        let navBarLeading = self.navigationBar?.leadingStack.flatMap { self.collectAllImageURLs(in: $0) } ?? []
+        let navBarTrailing = self.navigationBar?.trailingStack.flatMap { self.collectAllImageURLs(in: $0) } ?? []
+
+        return rootStackImageURLs + stickFooterImageURLs + navBarLeading + navBarTrailing
     }
 
     // swiftlint:disable:next cyclomatic_complexity
@@ -67,10 +70,8 @@ extension PaywallComponentsData.PaywallComponentsConfig {
                 urls += self.collectAllImageURLs(in: package.stack)
             case .purchaseButton(let purchaseButton):
                 urls += self.collectAllImageURLs(in: purchaseButton.stack)
-            case .stickyFooter(let stickyFooter):
-                urls += self.collectAllImageURLs(in: stickyFooter.stack)
-            case .timeline(let component):
-                for item in component.items {
+            case .timeline(let timeline):
+                for item in timeline.items {
                     urls += item.icon.imageUrls
                 }
             case .tabs(let tabs):