--- title: ASP.NET Core Blazor routing author: guardrex description: Learn how to manage request routing in apps and how to use the NavLink component in Blazor apps for navigation. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 12/09/2020 no-loc: [Home, Privacy, Kestrel, appsettings.json, "ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR] uid: blazor/fundamentals/routing --- # ASP.NET Core Blazor routing In this article, learn how to manage request routing and how to use the component to create a navigation links in Blazor apps. ## Route templates The component enables routing to Razor components in a Blazor app. The component is used in the `App` component of Blazor apps. `App.razor`: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/routing/App1.razor)] ::: moniker-end [!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)] ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/routing/App1.razor)] ::: moniker-end When a Razor component (`.razor`) with an [`@page` directive](xref:mvc/views/razor#page) is compiled, the generated component class is provided a specifying the component's route template. When the app starts, the assembly specified as the Router's `AppAssembly` is scanned to gather route information for the app's components that have a . At runtime, the component: * Receives the from the along with any route parameters. * Renders the specified component with its [layout](xref:blazor/layouts), including any further nested layouts. Optionally specify a parameter with a layout class for components that don't specify a layout with the [`@layout` directive](xref:blazor/layouts#apply-a-layout-to-a-component). The framework's [Blazor project templates](xref:blazor/project-structure) specify the `MainLayout` component (`Shared/MainLayout.razor`) as the app's default layout. For more information on layouts, see . Components support multiple route templates using multiple [`@page` directives](xref:mvc/views/razor#page). The following example component loads on requests for `/BlazorRoute` and `/DifferentBlazorRoute`. `Pages/BlazorRoute.razor`: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/Pages/routing/BlazorRoute.razor?highlight=1-2)] ::: moniker-end ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/Pages/routing/BlazorRoute.razor?highlight=1-2)] ::: moniker-end > [!IMPORTANT] > For URLs to resolve correctly, the app must include a `` tag in its `wwwroot/index.html` file (Blazor WebAssembly) or `Pages/_Host.cshtml` file (Blazor Server) with the app base path specified in the `href` attribute. For more information, see . ## Provide custom content when content isn't found The component allows the app to specify custom content if content isn't found for the requested route. In the `App` component, set custom content in the component's template. `App.razor`: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/routing/App2.razor?highlight=5-8)] ::: moniker-end [!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)] ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/routing/App2.razor?highlight=5-8)] ::: moniker-end Arbitrary items are supported as content of the `` tags, such as other interactive components. To apply a default layout to content, see . ## Route to components from multiple assemblies Use the parameter to specify additional assemblies for the component to consider when searching for routable components. Additional assemblies are scanned in addition to the assembly specified to `AppAssembly`. In the following example, `Component1` is a routable component defined in a referenced [component class library](xref:blazor/components/class-libraries). The following example results in routing support for `Component1`. `App.razor`: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/routing/App3.razor?name=snippet)] ::: moniker-end [!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)] ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/routing/App3.razor?name=snippet)] ::: moniker-end ## Route parameters The router uses route parameters to populate the corresponding [component parameters](xref:blazor/components/index#component-parameters) with the same name. Route parameter names are case insensitive. In the following example, the `text` parameter assigns the value of the route segment to the component's `Text` property. When a request is made for `/RouteParameter/amazing`, the `

` tag content is rendered as `Blazor is amazing!`. `Pages/RouteParameter.razor`: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/Pages/routing/RouteParameter1.razor?highlight=1)] ::: moniker-end ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/Pages/routing/RouteParameter1.razor?highlight=1)] ::: moniker-end ::: moniker range=">= aspnetcore-5.0" Optional parameters are supported. In the following example, the `text` optional parameter assigns the value of the route segment to the component's `Text` property. If the segment isn't present, the value of `Text` is set to `fantastic`. `Pages/RouteParameter.razor`: [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/Pages/routing/RouteParameter2.razor?highlight=1)] ::: moniker-end ::: moniker range="< aspnetcore-5.0" Optional parameters aren't supported. In the following example, two [`@page` directives](xref:mvc/views/razor#page) are applied. The first directive permits navigation to the component without a parameter. The second directive assigns the `{text}` route parameter value to the component's `Text` property. `Pages/RouteParameter.razor`: [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/Pages/routing/RouteParameter2.razor?highlight=2)] ::: moniker-end Use [`OnParametersSet`](xref:blazor/components/lifecycle#after-parameters-are-set-onparameterssetasync) instead of [`OnInitialized{Async}`](xref:blazor/components/lifecycle#component-initialization-oninitializedasync) to permit app navigation to the same component with a different optional parameter value. Based on the preceding example, use `OnParametersSet` when the user should be able to navigate from `/RouteParameter` to `/RouteParameter/amazing` or from `/RouteParameter/amazing` to `/RouteParameter`: ```csharp protected override void OnParametersSet() { Text = Text ?? "fantastic"; } ``` ## Route constraints A route constraint enforces type matching on a route segment to a component. In the following example, the route to the `User` component only matches if: * An `Id` route segment is present in the request URL. * The `Id` segment is an integer (`int`) type. `Pages/User.razor`: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/Pages/routing/User.razor?highlight=1)] ::: moniker-end ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/Pages/routing/User.razor?highlight=1)] ::: moniker-end The route constraints shown in the following table are available. For the route constraints that match the invariant culture, see the warning below the table for more information. | Constraint | Example | Example Matches | Invariant
culture
matching | | ---------- | ----------------- | -------------------------------------------------------------------------------- | :------------------------------: | | `bool` | `{active:bool}` | `true`, `FALSE` | No | | `datetime` | `{dob:datetime}` | `2016-12-31`, `2016-12-31 7:32pm` | Yes | | `decimal` | `{price:decimal}` | `49.99`, `-1,000.01` | Yes | | `double` | `{weight:double}` | `1.234`, `-1,001.01e8` | Yes | | `float` | `{weight:float}` | `1.234`, `-1,001.01e8` | Yes | | `guid` | `{id:guid}` | `CD2C1638-1638-72D5-1638-DEADBEEF1638`, `{CD2C1638-1638-72D5-1638-DEADBEEF1638}` | No | | `int` | `{id:int}` | `123456789`, `-123456789` | Yes | | `long` | `{ticks:long}` | `123456789`, `-123456789` | Yes | > [!WARNING] > Route constraints that verify the URL and are converted to a CLR type (such as `int` or ) always use the invariant culture. These constraints assume that the URL is non-localizable. ## Routing with URLs that contain dots For hosted Blazor WebAssembly and Blazor Server apps, the server-side default route template assumes that if the last segment of a request URL contains a dot (`.`) that a file is requested. For example, the URL `https://localhost.com:5001/example/some.thing` is interpreted by the router as a request for a file named `some.thing`. Without additional configuration, an app returns a *404 - Not Found* response if `some.thing` was meant to route to a component with an [`@page` directive](xref:mvc/views/razor#page) and `some.thing` is a route parameter value. To use a route with one or more parameters that contain a dot, the app must configure the route with a custom template. Consider the following `Example` component that can receive a route parameter from the last segment of the URL. `Pages/Example.razor`: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/Pages/routing/Example.razor?highlight=1)] ::: moniker-end ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/Pages/routing/Example.razor?highlight=2)] ::: moniker-end To permit the **`Server`** app of a hosted Blazor WebAssembly solution to route the request with a dot in the `param` route parameter, add a fallback file route template with the optional parameter in `Startup.Configure`. `Startup.cs`: ```csharp endpoints.MapFallbackToFile("/example/{param?}", "index.html"); ``` To configure a Blazor Server app to route the request with a dot in the `param` route parameter, add a fallback page route template with the optional parameter in `Startup.Configure`. `Startup.cs`: ```csharp endpoints.MapFallbackToPage("/example/{param?}", "/_Host"); ``` For more information, see . ## Catch-all route parameters ::: moniker range=">= aspnetcore-5.0" Catch-all route parameters, which capture paths across multiple folder boundaries, are supported in components. Catch-all route parameters are: * Named to match the route segment name. Naming isn't case sensitive. * A `string` type. The framework doesn't provide automatic casting. * At the end of the URL. `Pages/CatchAll.razor`: [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/Pages/routing/CatchAll.razor)] For the URL `/catch-all/this/is/a/test` with a route template of `/catch-all/{*pageRoute}`, the value of `PageRoute` is set to `this/is/a/test`. Slashes and segments of the captured path are decoded. For a route template of `/catch-all/{*pageRoute}`, the URL `/catch-all/this/is/a%2Ftest%2A` yields `this/is/a/test*`. ::: moniker-end ::: moniker range="< aspnetcore-5.0" Catch-all route parameters are supported in ASP.NET Core 5.0 or later. For more information, select the 5.0 version of this article. ::: moniker-end ## URI and navigation state helpers Use to manage URIs and navigation in C# code. provides the event and methods shown in the following table. | Member | Description | | ------ | ----------- | | | Gets the current absolute URI. | | | Gets the base URI (with a trailing slash) that can be prepended to relative URI paths to produce an absolute URI. Typically, corresponds to the `href` attribute on the document's `` element in `wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Host.cshtml` (Blazor Server). | | | Navigates to the specified URI. If `forceLoad` is `true`:
  • Client-side routing is bypassed.
  • The browser is forced to load the new page from the server, whether or not the URI is normally handled by the client-side router.
| | | An event that fires when the navigation location has changed. | | | Converts a relative URI into an absolute URI. | | | Given a base URI (for example, a URI previously returned by ), converts an absolute URI into a URI relative to the base URI prefix. | For the event, provides the following information about navigation events: * : The URL of the new location. * : If `true`, Blazor intercepted the navigation from the browser. If `false`, caused the navigation to occur. The following component: * Navigates to the app's `Counter` component (`Pages/Counter.razor`) when the button is selected using . * Handles the location changed event by subscribing to . * The `HandleLocationChanged` method is unhooked when `Dispose` is called by the framework. Unhooking the method permits garbage collection of the component. * The logger implementation logs the following information when the button is selected: > `BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:5001/counter` `Pages/Navigate.razor`: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/Pages/routing/Navigate.razor)] ::: moniker-end ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/Pages/routing/Navigate.razor)] ::: moniker-end For more information on component disposal, see . ## Query string and parse parameters The query string of a request is obtained from the property: ```razor @inject NavigationManager NavigationManager ... var query = new Uri(NavigationManager.Uri).Query; ``` To parse a query string's parameters, one approach is to use [`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams) with [JavaScript (JS) interop](xref:blazor/js-interop/call-javascript-from-dotnet): ::: moniker range=">= aspnetcore-5.0" ```javascript export createQueryString = (string queryString) => new URLSearchParams(queryString); ``` For more information on JavaScript isolation with JavaScript modules, see . ::: moniker-end ::: moniker range="< aspnetcore-5.0" ```html ``` For more information, see . ::: moniker-end ## `NavLink` and `NavMenu` components Use a component in place of HTML hyperlink elements (``) when creating navigation links. A component behaves like an `` element, except it toggles an `active` CSS class based on whether its `href` matches the current URL. The `active` class helps a user understand which page is the active page among the navigation links displayed. Optionally, assign a CSS class name to to apply a custom CSS class to the rendered link when the current route matches the `href`. The following `NavMenu` component creates a [`Bootstrap`](https://getbootstrap.com/docs/) navigation bar that demonstrates how to use components: ::: moniker range=">= aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/5.x/BlazorSample_WebAssembly/Shared/routing/NavMenu.razor?name=snippet&highlight=4,9)] ::: moniker-end ::: moniker range="< aspnetcore-5.0" [!code-razor[](~/blazor/common/samples/3.x/BlazorSample_WebAssembly/Shared/routing/NavMenu.razor?name=snippet&highlight=4,9)] ::: moniker-end > [!NOTE] > The `NavMenu` component (`NavMenu.razor`) is provided in the `Shared` folder of an app generated from the [Blazor project templates](xref:blazor/project-structure). There are two options that you can assign to the `Match` attribute of the `` element: * : The is active when it matches the entire current URL. * (*default*): The is active when it matches any prefix of the current URL. In the preceding example, the Home `href=""` matches the home URL and only receives the `active` CSS class at the app's default base path URL (for example, `https://localhost:5001/`). The second receives the `active` class when the user visits any URL with a `component` prefix (for example, `https://localhost:5001/component` and `https://localhost:5001/component/another-segment`). Additional component attributes are passed through to the rendered anchor tag. In the following example, the component includes the `target` attribute: ```razor Example page ``` The following HTML markup is rendered: ```html Example page ``` > [!WARNING] > Due to the way that Blazor renders child content, rendering `NavLink` components inside a `for` loop requires a local index variable if the incrementing loop variable is used in the `NavLink` (child) component's content: > > ```razor > @for (int c = 0; c < 10; c++) > { > var current = c; >
  • > > @current > >
  • > } > ``` > > Using an index variable in this scenario is a requirement for **any** child component that uses a loop variable in its [child content](xref:blazor/components/index#child-content), not just the `NavLink` component. > > Alternatively, use a `foreach` loop with : > > ```razor > @foreach(var c in Enumerable.Range(0,10)) > { >
  • > > @c > >
  • > } > ``` ## ASP.NET Core endpoint routing integration *This section only applies to Blazor Server apps.* Blazor Server is integrated into [ASP.NET Core Endpoint Routing](xref:fundamentals/routing). An ASP.NET Core app is configured to accept incoming connections for interactive components with in `Startup.Configure`. `Startup.cs`: ::: moniker range=">= aspnetcore-5.0" [!code-csharp[](~/blazor/common/samples/5.x/BlazorSample_Server/routing/Startup.cs?name=snippet&highlight=5)] ::: moniker-end ::: moniker range="< aspnetcore-5.0" [!code-csharp[](~/blazor/common/samples/3.x/BlazorSample_Server/routing/Startup.cs?name=snippet&highlight=5)] ::: moniker-end The typical configuration is to route all requests to a Razor page, which acts as the host for the server-side part of the Blazor Server app. By convention, the *host* page is usually named `_Host.cshtml` in the `Pages` folder of the app. The route specified in the host file is called a *fallback route* because it operates with a low priority in route matching. The fallback route is used when other routes don't match. This allows the app to use other controllers and pages without interfering with component routing in the Blazor Server app. For information on configuring for non-root URL server hosting, see .