diff --git a/src/Components/src/Microsoft.AspNetCore.Components.Browser.JS/src/Services/UriHelper.ts b/src/Components/src/Microsoft.AspNetCore.Components.Browser.JS/src/Services/UriHelper.ts
index 169916d8adc16be55075fa65601239c88b69b6c1..d867adab0e765f9b2e1b4945a60c8eeb2dd19ae3 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components.Browser.JS/src/Services/UriHelper.ts
+++ b/src/Components/src/Microsoft.AspNetCore.Components.Browser.JS/src/Services/UriHelper.ts
@@ -41,9 +41,10 @@ function enableNavigationInterception(assemblyName: string, functionName: string
   window.addEventListener('popstate', handleInternalNavigation);
 }
 
-export function navigateTo(uri: string) {
+export function navigateTo(uri: string, forceLoad: boolean) {
   const absoluteUri = toAbsoluteUri(uri);
-  if (isWithinBaseUriSpace(absoluteUri)) {
+
+  if (!forceLoad && isWithinBaseUriSpace(absoluteUri)) {
     performInternalNavigation(absoluteUri);
   } else {
     location.href = uri;
diff --git a/src/Components/src/Microsoft.AspNetCore.Components.Browser/Services/BrowserUriHelper.cs b/src/Components/src/Microsoft.AspNetCore.Components.Browser/Services/BrowserUriHelper.cs
index 8cd50f9c545fa567834cd11c12f1b846c63fdb09..cd4337b4f8141858b06f64f9c4e73c037633a533 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components.Browser/Services/BrowserUriHelper.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components.Browser/Services/BrowserUriHelper.cs
@@ -47,14 +47,14 @@ namespace Microsoft.AspNetCore.Components.Browser.Services
         }
 
         /// <inheritdoc />
-        protected override void NavigateToCore(string uri)
+        protected override void NavigateToCore(string uri, bool forceLoad)
         {
             if (uri == null)
             {
                 throw new ArgumentNullException(nameof(uri));
             }
 
-            ((IJSInProcessRuntime)JSRuntime.Current).Invoke<object>(Interop.NavigateTo, uri);
+            ((IJSInProcessRuntime)JSRuntime.Current).Invoke<object>(Interop.NavigateTo, uri, forceLoad);
         }
 
         /// <summary>
diff --git a/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/RemoteUriHelper.cs b/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/RemoteUriHelper.cs
index 9b7eaa7e8449e03906a685fa4984c238da0fff22..85149fcf69054cb873a8e6495c777c2376433ad9 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/RemoteUriHelper.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/RemoteUriHelper.cs
@@ -61,14 +61,10 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
             uriHelper.TriggerOnLocationChanged();
         }
 
-        /// <summary>
-        /// Navigates to the specified URI.
-        /// </summary>
-        /// <param name="uri">The destination URI. This can be absolute, or relative to the base URI
-        /// (as returned by <see cref="IUriHelper.GetBaseUri"/>).</param>
-        protected override void NavigateToCore(string uri)
+        /// <inheritdoc />
+        protected override void NavigateToCore(string uri, bool forceLoad)
         {
-            _jsRuntime.InvokeAsync<object>(Interop.NavigateTo, uri);
+            _jsRuntime.InvokeAsync<object>(Interop.NavigateTo, uri, forceLoad);
         }
     }
 }
diff --git a/src/Components/src/Microsoft.AspNetCore.Components/Services/IUriHelper.cs b/src/Components/src/Microsoft.AspNetCore.Components/Services/IUriHelper.cs
index e8deebaf346344a6053ca9a0bf17a01e725a0881..b228570ffc365622dba9584c3e1a87a3048c9631 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components/Services/IUriHelper.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components/Services/IUriHelper.cs
@@ -51,5 +51,13 @@ namespace Microsoft.AspNetCore.Components.Services
         /// <param name="uri">The destination URI. This can be absolute, or relative to the base URI
         /// (as returned by <see cref="GetBaseUri"/>).</param>
         void NavigateTo(string uri);
+
+        /// <summary>
+        /// Navigates to the specified URI.
+        /// </summary>
+        /// <param name="uri">The destination URI. This can be absolute, or relative to the base URI
+        /// (as returned by <see cref="GetBaseUri"/>).</param>
+        /// <param name="forceLoad">If true, bypasses client-side routing and forces the browser to load the new page from the server, whether or not the URI would normally be handled by the client-side router.</param>
+        void NavigateTo(string uri, bool forceLoad);
     }
 }
diff --git a/src/Components/src/Microsoft.AspNetCore.Components/Services/UriHelperBase.cs b/src/Components/src/Microsoft.AspNetCore.Components/Services/UriHelperBase.cs
index 78f2c2fa9fab554aba2e14338007cd8b8977d807..aebf73aad552ada657bcd9e9ef63502a8e8d38db 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components/Services/UriHelperBase.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components/Services/UriHelperBase.cs
@@ -45,9 +45,20 @@ namespace Microsoft.AspNetCore.Components.Services
         /// <param name="uri">The destination URI. This can be absolute, or relative to the base URI
         /// (as returned by <see cref="GetBaseUri"/>).</param>
         public void NavigateTo(string uri)
+        {
+            NavigateTo(uri, forceLoad: false);
+        }
+
+        /// <summary>
+        /// Navigates to the specified URI.
+        /// </summary>
+        /// <param name="uri">The destination URI. This can be absolute, or relative to the base URI
+        /// (as returned by <see cref="GetBaseUri"/>).</param>
+        /// <param name="forceLoad">If true, bypasses client-side routing and forces the browser to load the new page from the server, whether or not the URI would normally be handled by the client-side router.</param>
+        public void NavigateTo(string uri, bool forceLoad)
         {
             EnsureInitialized();
-            NavigateToCore(uri);
+            NavigateToCore(uri, forceLoad);
         }
 
         /// <summary>
@@ -55,7 +66,8 @@ namespace Microsoft.AspNetCore.Components.Services
         /// </summary>
         /// <param name="uri">The destination URI. This can be absolute, or relative to the base URI
         /// (as returned by <see cref="GetBaseUri"/>).</param>
-        protected abstract void NavigateToCore(string uri);
+        /// <param name="forceLoad">If true, bypasses client-side routing and forces the browser to load the new page from the server, whether or not the URI would normally be handled by the client-side router.</param>
+        protected abstract void NavigateToCore(string uri, bool forceLoad);
 
         /// <summary>
         /// Called to initialize BaseURI and current URI before those values the first time.
diff --git a/src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Tests/RoutingTest.cs b/src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Tests/RoutingTest.cs
index f7d0f69e633ca1b47541052da035b597bdf14021..82991d47cc4186e36339f33cedcc0cc7ca21b515 100644
--- a/src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Tests/RoutingTest.cs
+++ b/src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Tests/RoutingTest.cs
@@ -268,9 +268,33 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
             SetUrlViaPushState("/");
 
             var app = MountTestComponent<TestRouter>();
-            app.FindElement(By.TagName("button")).Click();
+            var testSelector = WaitUntilTestSelectorReady();
+
+            app.FindElement(By.Id("do-navigation")).Click();
+            WaitAssert.True(() => Browser.Url.EndsWith("/Other"));
             WaitAssert.Equal("This is another page.", () => app.FindElement(By.Id("test-info")).Text);
             AssertHighlightedLinks("Other", "Other with base-relative URL (matches all)");
+
+            // Because this was client-side navigation, we didn't lose the state in the test selector
+            Assert.Equal(typeof(TestRouter).FullName, testSelector.SelectedOption.GetAttribute("value"));
+        }
+
+        [Fact]
+        public void CanNavigateProgrammaticallyWithForceLoad()
+        {
+            SetUrlViaPushState("/");
+
+            var app = MountTestComponent<TestRouter>();
+            var testSelector = WaitUntilTestSelectorReady();
+
+            app.FindElement(By.Id("do-navigation-forced")).Click();
+            WaitAssert.True(() => Browser.Url.EndsWith("/Other"));
+
+            // Because this was a full-page load, our element references should no longer be valid
+            Assert.Throws<StaleElementReferenceException>(() =>
+            {
+                testSelector.SelectedOption.GetAttribute("value");
+            });
         }
 
         [Fact]
diff --git a/src/Components/test/testapps/BasicTestApp/RouterTest/Links.cshtml b/src/Components/test/testapps/BasicTestApp/RouterTest/Links.cshtml
index 10c401898f766bd843eb13dd00258cdc13d3ee07..1858b7e8327594381685adfaa134be9aa1e6e490 100644
--- a/src/Components/test/testapps/BasicTestApp/RouterTest/Links.cshtml
+++ b/src/Components/test/testapps/BasicTestApp/RouterTest/Links.cshtml
@@ -13,10 +13,14 @@
     <li><NavLink href="/subdir/WithParameters/Name/Abc/LastName/McDef">With parameters</NavLink></li>
 </ul>
 
-<button onclick=@(x => uriHelper.NavigateTo("Other"))>
+<button id="do-navigation" onclick=@(x => uriHelper.NavigateTo("Other"))>
     Programmatic navigation
 </button>
 
+<button id="do-navigation-forced" onclick=@(x => uriHelper.NavigateTo("Other", true))>
+    Programmatic navigation with force-load
+</button>
+
 <a id="anchor-with-no-href">
     Anchor tag with no href attribute
 </a>