Navs 1.0.0

dotnet add package Navs --version 1.0.0
                    
NuGet\Install-Package Navs -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Navs" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Navs" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Navs" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Navs --version 1.0.0
                    
#r "nuget: Navs, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Navs@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Navs&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Navs&version=1.0.0
                    
Install as a Cake Tool

The Navs Family

Welcome to the Navs Family documentation.

This documentation is a work in progress.

This project contains the following libraries:

Navs is a router-like abstraction inspired by web routers such as vue-router, angular-router and similar projects.

It is primarily a "core" library which you would usually depend on in your own projects, as it is very generic and while F# can be very intelligent about type inference, it tends to produce quite verbose signatures. For more information visit the Navs section in these docs.

A Compelling Example:


let routes = [
  Route.define<string>("home", "/", (fun _ -> "Home"))
  Route.define<string>("about", "/about", (fun _ -> "About"))
  Route.define<string>(
    "guid",
    "/:id<guid>",
    fun context _ -> async {
      do! Async.Sleep(90)
      return
        match context.getParam<Guid> "id" with
        | ValueSome id -> sprintf "Home %A" id
        | ValueNone -> "Guid No GUID"
    }
  )
]

let router = Router.build<string>(routes)

router.Content.AddCallback(fun content -> printfn $"%A{content}")

let! result1 = router.navigate("/about")
let! result2 = router.navigate("/home")
let! result3 = router.navigate("/123e4567-e89b-12d3-a456-426614174000")

// "About"
// "Home"
// "Home 123e4567-e89b-12d3-a456-426614174000"

This project attempts to hide the generics from call sites and offer a few DSLs to make it easier to use Navs in Avalonia applications. This router was designed to be used with Raw Avalonia Control classes however, it will pair very nicely with the NXUI project, Feel free to check the C# and F# samples in the Samples folder in the source code repository.

A Compelling Example:


let routes = [
  Route.define(
    "guid",
    // routes can be typed!
    "/:id<guid>",
    fun context _ -> async {
      // you can pre-load data if you want to
      do! Async.Sleep(90)
      return
        // extract parameters from the URL
        match context.getParam<guid> "id" with
        | ValueSome id -> TextBlock().text(sprintf "Home %A" id)
        | ValueNone -> TextBlock().text("Guid No GUID")
    }
  )
  // Simpler non-async routes are also supported
  Route.define("books", "/books", (fun _ _ -> TextBlock().text("Books")))
]

let navigate url (router: IRouter<Control>) _ _ =
  task {
    // navigation is asynchronous and returns a result
    // in order to check if the navigation was successful
    let! result = router.Navigate(url)

    match result with
    | Ok _ -> ()
    | Error e -> printfn $"%A{e}"
  }
  |> ignore

let app () =

  let router: IRouter<Control> = AvaloniaRouter(routes, splash = fun _ -> TextBlock().text("Loading..."))

  Window()
    .content(
      DockPanel()
        .lastChildFill(true)
        .children(
          StackPanel()
            .DockTop()
            .OrientationHorizontal()
            .spacing(8)
            .children(
              Button().content("Books").OnClickHandler(navigate "/books" router),
              Button()
                .content("Guid")
                .OnClickHandler(navigate $"/{Guid.NewGuid()}" router)
            ),
          RouterOutlet().router(router)
        )
    )


NXUI.Run(app, "Navs.Avalonia!", Environment.GetCommandLineArgs()) |> ignore

In a similar Fashion of Navs.Avalonia, this project attempts to provide a smooth API interface for Avalonia.FuncUI, you can find a sample in the Samples folder in the source code repository.

A Compelling Example:


let routes = [
  Route.define(
    "books",
    "/books",
    (fun _ _ -> TextBlock.create [ TextBlock.text "Books" ])
  )
  Route.define(
    "guid",
    "/:id<guid>",
    fun context _ -> async {
      return
        TextBlock.create [
          match context.getParam<guid> "id" with
          | ValueSome id -> TextBlock.text $"Visited: {id}"
          | ValueNone -> TextBlock.text "Guid No GUID"
        ]
    }
  )
]

let appContent (router: IRouter<IView>, navbar: IRouter<IView> -> IView) =
  Component(fun ctx ->

    let currentView = ctx.useRouter router

    DockPanel.create [
      DockPanel.lastChildFill true
      DockPanel.children [ navbar router; currentView.Current ]
    ]
  )

UrlTemplates

This is a library for parsing URL-like strings into structured objects. It is used by Navs to parse navigable URLs and URL templates to find if they match.

Currently this library is mainly aimed to be used from F# but if there's interest in using it from C# I can add some more friendly APIs.

A Compelling Example:

open UrlTemplates.RouteMatcher

let template = "/api/v1/profiles/:id<int>?optionalKey<guid>&requiredKey!#hash"
let url = "/api/v1/profiles/2345?requiredKey=2#hash"

match RouteMatcher.matchStrings template url with
| Ok (urlTemplate, urlInfo, urlMatch) ->
  let { Segments = foundParams; Query = queryParams; Hash = foundHash } = urlTemplate
  // foundParams
  // [ Plain ""; Plain "api"; Plain "v1"; Plain "profiles"; Param ("id", "2345");]
  // query
  // [Optional "optionalKeyu", Guid; Required "requiredKey", Int]
  // hash
  // "hash"


  let { Params = urlParams; Query = query; Hash = hash } = urlInfo
  // urlParams
  // [ ""; "api"; "v1"; "profiles"; "2345" ]
  // query
  // [ "optionalKey", String ValueNone; "requiredKey", String ValueSome "2"]
  // hash
  // ValueSome "hash"

  let { Params = foundParams; QueryParams = queryParams; Hash = foundHash } = urlMatch
  // foundParams
  // { { "id", box 2345 } }
  // queryParams
  // { { "requiredKey", box "2" } }
  // foundHash
  // ValueSome "hash"

| Error errors ->
  for e in errors do
    printfn $"%A{e}"
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Navs:

Package Downloads
Navs.Avalonia

Package Description

Navs.FuncUI

Package Description

Navs.Terminal.Gui

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 81 6/25/2026
1.0.0-rc-008 419 6/17/2025
1.0.0-rc-007 252 6/4/2025
1.0.0-rc-006 252 5/16/2025
1.0.0-rc-005 269 11/30/2024
1.0.0-rc-004 179 11/27/2024
1.0.0-rc-003 184 11/14/2024
1.0.0-rc-002 170 11/13/2024
1.0.0-rc-001 283 3/28/2024
1.0.0-beta-008 180 3/17/2024
1.0.0-beta-007 182 3/15/2024
1.0.0-beta-006 168 3/13/2024
1.0.0-beta-005 178 3/10/2024
1.0.0-beta-004 185 3/9/2024
1.0.0-beta-003 190 3/7/2024
1.0.0-beta-002 170 3/6/2024
1.0.0-beta-001 182 3/6/2024

### Changed

- **Navs (breaking):** `RouteGuard` is now a two-argument delegate (`RouteContext voption * RouteContext`) and uses `IcedTasks.CancellableValueTask<GuardResponse>`. Update existing guards to accept the previous route (or `ValueNone`) and the target route, and to return a `CancellableValueTask`.
- **Navs (breaking):** Nested routes and `RouteDefinition.children` have been removed. Define all routes at the top level.
- **Navs.Avalonia (breaking):** The view type is now `Control` instead of `#Control`. Existing route handlers returning specific Avalonia control subtypes may need explicit upcasts.
- **Core:** Central package management enabled; all dependencies centralized in `Directory.Packages.props`.
- **Core:** Samples upgraded to `net10.0` and solution files converted to `.slnx`.
- **Navs:** Router refactored for improved modularity, cancellable navigation, and async guard activation/deactivation.
- **Navs.Avalonia:** Updated to Avalonia 12.0.5 and fixed nullability warnings.
- **Navs.FuncUI:** Updated Avalonia/FuncUI dependencies and added logging support.
- **Project:** Fantomas 7.0.2 and `<Nullable>enable` applied across the solution.

### Added

- **Navs.Terminal.Gui:** New adapter library for Terminal.Gui applications.
- **Navs:** Optional `ILogger` support across `Router`, adapters, and `RouterOutlet` for diagnostics during navigation, guards, cache hits, redirects, and route resolution.
- **Navs:** `RouteContext` query-parameter helpers (`getParamSequence`) and an `IDisposableBag` for per-route cleanup.
- **Navs:** `NavigationError<'View>` discriminated union with explicit cases for same-route, cancellation, not found, activation/deactivation failures, and guard redirects.
- **Navs:** Optional `maxCyclicRedirects` parameter on `Router.build` (defaults to 5) to bound how many distinct redirect pairs the router follows before bailing; protects against redirect cycles.
- **UrlTemplates:** Query-parameter parsing improvements and corrected error-message spelling.
- **CI:** Release workflow that extracts the version from this changelog, runs tests, packs, and publishes to NuGet and GitHub Releases.
- **Project:** `AGENTS.md` with agent imperatives and changelog conventions.

### Fixed

- **Navs:** The previous active route's resources are now disposed only after activation guards and content resolution pass, so a failed navigation no longer leaves a broken view on screen.
- **Navs:** Redirect chains are protected against cycles and unbounded distinct-redirect counts; the router reports a clear error instead of looping indefinitely.
- **Navs.FuncUI:** Initial-navigation failures now log the underlying exception rather than discarding it.
- **Navs.Terminal.Gui:** The `Text` binding extension now reads/writes `Text` instead of `Title`.
- **UrlTemplates:** Corrected a "Requred" typo in the missing-parameter error message.