Navigation is one of the most common tasks in a Blazor application. A user clicks a menu item, opens a details page, submits a form, or moves between steps in a workflow. Blazor provides two common ways to handle this: NavLink for links in the UI and NavigationManager.NavigateTo for programmatic navigation.

The basic usage is straightforward, but navigation becomes more confusing when an application has nested routes or is hosted under a subpath.

For example, an application might contain routes such as:

/products
/products/details
/products/reviews

A developer may expect details to mean "go to the details page related to the current page." However, how a URI is resolved depends on whether it is root-relative, application-relative, or explicitly relative to the current URI.

Blazor in .NET 11 adds RelativeToCurrentUri support to NavigationManager.NavigateTo and NavLink. This makes it possible to explicitly control whether a relative navigation target is resolved against the current URI.

Understanding this distinction helps prevent broken links, unexpected redirects, and routing problems, especially in applications with nested URLs.

Understanding Relative URLs in Blazor

Before using NavigateTo or NavLink, it is important to understand how the URL is written.

Consider an application running at:

https://example.com/

There are several ways to represent a destination.

Root-Relative URL

A URL beginning with / is root-relative:

/orders

It refers to the domain root.

For example:

https://example.com/orders

Application-Relative URL

A URL without a leading / is relative to the application's base URI in normal Blazor navigation:

orders

If the application is hosted at the root, it can resolve to:

https://example.com/orders

If the application is hosted under a base path such as /portal/, the same application-relative route can resolve within that base path.

Current-URI-Relative Navigation

.NET 11 provides an explicit option for resolving navigation relative to the current URI:

new NavigationOptions
{
    RelativeToCurrentUri = true
}

This is useful when an application has hierarchical routes and the target should be interpreted relative to the current location.

These three concepts should not be treated as interchangeable.

Using NavigationManager.NavigateTo

NavigationManager is used when navigation needs to happen from application code.

Inject it into a component:

@inject NavigationManager Navigation

Then call:

Navigation.NavigateTo("orders");

A complete example is:

@page "/dashboard"
@inject NavigationManager Navigation

<h2>Dashboard</h2>

<button @onclick="OpenOrders">
    View Orders
</button>

@code {
    private void OpenOrders()
    {
        Navigation.NavigateTo("orders");
    }
}

This approach is useful when navigation depends on an action.

For example:

Save data
   |
   v
Check result
   |
   v
Navigate to confirmation

The navigation is part of the application workflow rather than simply being a link displayed to the user.

When to Use NavLink

NavLink is designed for navigation elements such as menus, sidebars, and navigation bars.

For example:

<nav>
    <NavLink href="dashboard">
        Dashboard
    </NavLink>

    <NavLink href="orders">
        Orders
    </NavLink>

    <NavLink href="customers">
        Customers
    </NavLink>
</nav>

One important advantage of NavLink is that it understands the current navigation location and can apply an active CSS class to the matching link.

A typical navigation menu might therefore look like this:

<nav class="sidebar">
    <NavLink href="dashboard">
        Dashboard
    </NavLink>

    <NavLink href="orders">
        Orders
    </NavLink>

    <NavLink href="customers">
        Customers
    </NavLink>
</nav>

This is generally preferable to creating a button and calling NavigateTo when the only purpose of the element is to take the user to another page.

NavigateTo vs NavLink

Although both APIs can navigate between pages, they serve different purposes.

Feature

NavigateTo

NavLink

Primary purpose

Programmatic navigation

Navigation UI

Typical usage

Button, form, workflow

Menu, sidebar, navigation bar

Active CSS state

No

Yes

Event-driven navigation

Yes

Not normally required

RelativeToCurrentUri

Supported

Supported

Replace browser history entry

Supported

Not directly configured in the same way

Force full page load

Supported

Not its primary purpose

A useful rule is:

User selects a destination → NavLink
Application decides a destination → NavigateTo

This isn't an absolute rule, but it makes component design easier to understand.

The Problem with Leading Slashes

One of the most common navigation mistakes is adding / without considering what it means.

For example:

Navigation.NavigateTo("/orders");

The destination is root-relative.

If the application is hosted at the domain root, this may be exactly what you need.

However, suppose the application is deployed under:

/portal/

The developer may actually want:

/portal/orders

but a root-relative destination can point to:

/orders

This distinction becomes especially important for applications deployed behind reverse proxies or under a virtual directory.

For application-relative navigation, use:

Navigation.NavigateTo("orders");

instead of automatically using:

Navigation.NavigateTo("/orders");

The same principle applies to NavLink.

Prefer:

<NavLink href="orders">
    Orders
</NavLink>

when the link should be relative to the application's base path.

Nested Routes and Current URI Navigation

Now consider a documentation application with routes like:

/docs
/docs/getting-started
/docs/getting-started/install
/docs/getting-started/configuration

Suppose the user is currently on:

/docs/getting-started/install

and the application needs to navigate to another page relative to that location.

A normal application-relative navigation and current-URI-relative navigation have different meanings.

.NET 11 provides RelativeToCurrentUri for this purpose.

For example:

Navigation.NavigateTo(
    "configuration",
    new NavigationOptions
    {
        RelativeToCurrentUri = true
    });

The important part is:

RelativeToCurrentUri = true

It tells the navigation system that the supplied URI should be resolved relative to the current URI rather than simply treating it as a normal application-base-relative destination.

This is particularly useful when reusable components operate inside nested route structures.

Using RelativeToCurrentUri with NavLink

The same concept is available with NavLink.

For example:

<NavLink href="configuration"
         RelativeToCurrentUri="true">
    Configuration
</NavLink>

This is useful for navigation components that are designed to work relative to the page where they are rendered.

For example, imagine a reusable component:

<div class="section-navigation">
    <NavLink href="overview"
             RelativeToCurrentUri="true">
        Overview
    </NavLink>

    <NavLink href="configuration"
             RelativeToCurrentUri="true">
        Configuration
    </NavLink>

    <NavLink href="history"
             RelativeToCurrentUri="true">
        History
    </NavLink>
</div>

The component does not have to hardcode a complete route hierarchy.

Its navigation can be interpreted in relation to the current location.

Choosing Between Normal Relative Navigation and Current URI Navigation

The most important question is:

What should the destination be relative to?

Consider this example:

Application base:
 /admin/

Current URI:
 /admin/users/

Desired destination:
 /admin/reports/

If the destination is a normal application route, use application-relative navigation:

Navigation.NavigateTo("reports");

If the navigation component is intentionally designed around the current URI hierarchy, current-URI-relative navigation can be used:

Navigation.NavigateTo(
    "reports",
    new NavigationOptions
    {
        RelativeToCurrentUri = true
    });

The correct choice depends on the route structure.

Do not use RelativeToCurrentUri simply because it is available. Use it when the navigation relationship is actually based on the current URI.

Using NavigationOptions

.NET provides NavigationOptions for controlling navigation behavior.

For example:

var options = new NavigationOptions
{
    RelativeToCurrentUri = true,
    ReplaceHistoryEntry = false,
    ForceLoad = false
};

Navigation.NavigateTo("configuration", options);

The options make the navigation intent explicit.

RelativeToCurrentUri

Controls whether the target is resolved relative to the current URI.

RelativeToCurrentUri = true

ReplaceHistoryEntry

Controls whether the current browser history entry is replaced.

ReplaceHistoryEntry = true

This can be useful for redirects where the user shouldn't return to the intermediate URL by pressing the browser Back button.

ForceLoad

Controls whether Blazor should bypass client-side routing and perform a full browser load.

ForceLoad = true

This should not be used as a general fix for routing problems.

If a URL is incorrect, ForceLoad does not make the URL correct.

Relative Navigation with Query Parameters

Relative navigation can also contain query parameters.

For example:

Navigation.NavigateTo("orders?status=pending");

This can be useful when filters or search criteria are represented in the URL.

A component might use:

<button @onclick="ShowPendingOrders">
    Pending Orders
</button>

@code {
    private void ShowPendingOrders()
    {
        Navigation.NavigateTo("orders?status=pending");
    }
}

Keep query-string construction readable. If an application has many parameters, consider using Blazor's supported query-string binding features rather than manually concatenating large URLs throughout components.

Relative Navigation with Fragments

A URI can also contain a fragment:

Navigation.NavigateTo("orders#history");

Fragments are useful when a page contains different sections and the browser should move to a particular location.

For example:

<h2 id="history">Order History</h2>

Then:

Navigation.NavigateTo("orders#history");

can target the history section of the orders page.

Again, whether the URI should be application-relative or current-URI-relative depends on the route structure.

NavigationManager Uri and BaseUri

When troubleshooting navigation, NavigationManager provides two particularly useful properties.

Navigation.Uri

returns the current absolute URI.

Navigation.BaseUri

returns the application's base URI.

For example:

@inject NavigationManager Navigation

<p>Current URI: @Navigation.Uri</p>
<p>Base URI: @Navigation.BaseUri</p>

This is useful when an application behaves differently between development and production.

For example, you might discover that development uses:

https://localhost:5001/

while production uses:

https://example.com/portal/

That difference can explain why a root-relative link works locally but fails after deployment.

Working with Base-Relative Paths

NavigationManager also provides ToBaseRelativePath.

For example:

var relativePath =
    Navigation.ToBaseRelativePath(Navigation.Uri);

This is useful when an application needs to inspect the current location without manually removing the base URI.

For example:

private string GetCurrentRoute()
{
    return Navigation.ToBaseRelativePath(Navigation.Uri);
}

This approach is preferable to manually manipulating strings when working with application base paths.

A Practical Navigation Component

A real application might have a sidebar like this:

@inject NavigationManager Navigation

<nav class="sidebar">
    <NavLink href="dashboard">
        Dashboard
    </NavLink>

    <NavLink href="orders">
        Orders
    </NavLink>

    <NavLink href="customers">
        Customers
    </NavLink>
</nav>

@code {
    private void OpenSettings()
    {
        Navigation.NavigateTo("settings");
    }
}

The menu uses NavLink because these are normal destinations.

If a settings page is opened only after a particular application action, NavigateTo is more appropriate:

private async Task CompleteSetup()
{
    await SaveSettingsAsync();

    Navigation.NavigateTo("settings");
}

This keeps the navigation responsibility aligned with the purpose of each API.

Common Mistakes

Using Root-Relative URLs Everywhere

This:

<NavLink href="/orders">
    Orders
</NavLink>

is not automatically wrong.

The problem occurs when the application is expected to treat orders as a route within its application base.

Use:

<NavLink href="orders">
    Orders
</NavLink>

when application-relative navigation is intended.

Using NavigateTo for Every Link

This pattern:

<button @onclick="OpenOrders">
    Orders
</button>

with:

private void OpenOrders()
{
    Navigation.NavigateTo("orders");
}

works, but it is unnecessary when the element is simply a navigation link.

A NavLink communicates the intent more clearly:

<NavLink href="orders">
    Orders
</NavLink>

Assuming RelativeToCurrentUri Is Always Better

RelativeToCurrentUri solves a specific routing problem.

It should not replace ordinary application-relative navigation everywhere.

First identify the relationship:

Domain root
Application base
Current URI

Then select the appropriate navigation style.

Hardcoding Deployment Paths

Avoid:

Navigation.NavigateTo("/portal/orders");

if /portal is simply the deployment location.

Application components should generally not need to know infrastructure-specific prefixes.

Using ForceLoad as a Routing Fix

Avoid this approach:

Navigation.NavigateTo("/orders", forceLoad: true);

simply because normal navigation doesn't work.

First verify:

  • The route exists.

  • The URI is correct.

  • The application base is correct.

  • The hosting configuration is correct.

  • The server can handle the destination.

Troubleshooting Navigation Problems

When navigation behaves unexpectedly, follow a consistent process.

Step 1: Inspect the Current URI

Display:

Navigation.Uri

and determine exactly where the application currently is.

Step 2: Inspect the Base URI

Check:

Navigation.BaseUri

This is especially important when the application is hosted under a subpath.

Step 3: Look for Leading Slashes

Search the component for:

href="/

and:

NavigateTo("/

These are often the first places to investigate.

Step 4: Verify the Route

Make sure the destination actually exists.

For example:

@page "/orders"

does not automatically create:

/orders/details

unless another component defines that route.

Step 5: Test Direct Navigation

Don't test only by clicking a menu item.

Copy the resulting URL into the browser and refresh the page.

This helps distinguish a client-side navigation problem from a server-side hosting problem.

Step 6: Test Under the Production Base Path

If production uses:

/app/

test the application under the same structure during development or staging.

Many navigation issues remain hidden when the application is always tested at /.

Best Practices

  1. Use NavLink for normal application navigation menus.

  2. Use NavigateTo when navigation is triggered by application logic.

  3. Understand the difference between root-relative and application-relative URLs.

  4. Avoid adding a leading / unless root-relative navigation is intentional.

  5. Use RelativeToCurrentUri when the destination should be resolved against the current URI.

  6. Don't hardcode reverse-proxy or deployment prefixes into components.

  7. Use NavigationManager.Uri and BaseUri when troubleshooting.

  8. Don't use ForceLoad to hide an underlying routing problem.

  9. Test nested routes and direct browser refreshes.

  10. Keep navigation code simple when standard relative navigation is sufficient.

Advantages

Cleaner Navigation Code

Developers can express navigation intent without manually constructing complete URLs.

Better Support for Nested Applications

Current-URI-relative navigation is useful for components that operate inside hierarchical route structures.

Better Reusable Components

A reusable navigation component can work relative to its rendering context instead of depending on one hardcoded route hierarchy.

Improved Subpath Compatibility

Application-relative navigation reduces unnecessary dependencies on the deployment location.

Clear Separation of Responsibilities

NavLink handles navigation UI, while NavigateTo handles programmatic navigation.

Disadvantages and Trade-Offs

Relative URI Semantics Can Be Confusing

Developers unfamiliar with URL resolution may not immediately understand the difference between the application base and current URI.

Nested Routes Require Care

A relative destination can produce an unexpected result when the intended reference point is not clearly defined.

Migration May Require Code Changes

Applications with many hardcoded root-relative URLs may need to review existing navigation code before moving to a subpath deployment.

Infrastructure Still Matters

Correct Blazor navigation cannot compensate for incorrect reverse-proxy, routing, or base-path configuration.

Production Checklist

Before deploying a Blazor application, verify the following:

Check

What to Verify

Navigation menus

Use NavLink for normal links

Programmatic navigation

Use NavigateTo where appropriate

Leading /

Confirm root-relative navigation is intentional

Nested routes

Verify the intended reference URI

RelativeToCurrentUri

Use only when current-URI resolution is required

Base path

Confirm the application base is correct

Reverse proxy

Confirm path prefixes aren't duplicated

Deep links

Open nested URLs directly

Refresh

Refresh deep URLs in the browser

Query parameters

Verify filters and state survive navigation

History

Check Back-button behavior after redirects

ForceLoad

Use only when a full browser load is actually required

Final Takeaway

Blazor navigation is easier to maintain when every URL has a clearly defined reference point.

Use NavLink when you are building normal navigation UI and NavigationManager.NavigateTo when navigation is part of application logic.

For ordinary application-relative navigation, a simple route such as:

Navigation.NavigateTo("orders");

is often enough.

When a destination needs to be interpreted relative to the current URI, .NET 11's RelativeToCurrentUri option provides a more explicit solution:

Navigation.NavigateTo(
    "configuration",
    new NavigationOptions
    {
        RelativeToCurrentUri = true
    });

The same capability is available with NavLink:

<NavLink href="configuration"
         RelativeToCurrentUri="true">
    Configuration
</NavLink>

The important thing is not to memorize one syntax and use it everywhere. Instead, determine whether the destination is relative to the domain root, the application's base path, or the current URI.

That small distinction can prevent a large class of navigation bugs, especially in applications with nested routes, reusable components, and non-root deployments.