Skip to content

API proposal for NavigateTo and NavLink with relative path #65060

@ilonatommy

Description

@ilonatommy

Introduced in #64670.

Background and Motivation

When building Blazor applications with nested folder structures (like file explorers or documentation sites), developers face a significant limitation when navigating between sibling pages. For example, with a structure like:

/docs/
/docs/getting-started/
/docs/getting-started/installation.html
/docs/getting-started/configuration.html

When at /docs/getting-started/installation.html and trying to navigate to a sibling page using:

NavigationManager.NavigateTo("configuration.html");

The navigation redirects to /configuration.html (app root) instead of the expected /docs/getting-started/configuration.html which is not expected by some users.

This happens because Blazor resolves relative URLs against the <base> tag (or base URI), not the current document path. This behavior differs from how browsers naturally resolve relative paths and causes friction for developers building applications with hierarchical URL structures.

The same limitation affects NavLink components. This feature allows developers to navigate to URIs relative to the current page path rather than the application's base URI.

Fixes #23615

Proposed API

namespace Microsoft.AspNetCore.Components;

public partial struct NavigationOptions
{
+    public bool RelativeToCurrentUri { get; init; }
}
namespace Microsoft.AspNetCore.Components.Routing;

public class NavLink : ComponentBase
{
+    [Parameter] public bool RelativeToCurrentUri { get; set; }
}

Usage Examples

NavigationManager

@inject NavigationManager NavManager

// Navigate to a sibling page relative to the current URI
<button @onclick='() => NavManager.NavigateTo("details.html", new NavigationOptions { RelativeToCurrentUri = true })'>
    View Details
</button>

// Navigate up one directory level
<button @onclick='() => NavManager.NavigateTo("../general.html", new NavigationOptions { RelativeToCurrentUri = true })'>
    General Settings
</button>

NavLink

<NavLink href="details" RelativeToCurrentUri="true">View Details</NavLink>

<NavLink href="../orders" RelativeToCurrentUri="true">Orders</NavLink>

Resolution Example

Given:

  • Current URL: https://example.com/app/admin/settings/password/change.html
  • Base tag: <base href="/app/admin/">
Link RelativeToCurrentUri = true RelativeToCurrentUri = false (default)
../general.html /app/admin/settings/general.html /app/general.html
./edit /app/admin/settings/password/edit /app/admin/edit
../../orders /app/admin/settings/orders /orders

Alternative Designs

  1. Automatic relative path detection using ./ and ../ - Using standard relative path prefixes to automatically resolve against current URI instead of base URI. However, these already have established meaning (resolved against BaseUri), and changing this would be a breaking change.

  2. Separate navigation methods - Creating distinct methods like NavigateToRelative(). This was rejected to avoid API surface bloat when a simple option parameter suffices.

The chosen design is inspired by Angular's router which offers a similar relativeTo option, making Blazor consistent with other major SPA frameworks.

Risks

  1. Learning curve - Developers need to understand the distinction between base-relative and document-relative resolution. Clear documentation will be essential.

  2. No breaking changes - The default behavior (RelativeToCurrentUri = false) preserves existing functionality, so existing applications are unaffected.

  3. Performance - The implementation uses zero-allocation span-based operations and avoids creating Uri objects, minimizing performance impact.

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-blazorIncludes: Blazor, Razor Components

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions