NextFs 0.9.0

dotnet add package NextFs --version 0.9.0
                    
NuGet\Install-Package NextFs -Version 0.9.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="NextFs" Version="0.9.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NextFs" Version="0.9.0" />
                    
Directory.Packages.props
<PackageReference Include="NextFs" />
                    
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 NextFs --version 0.9.0
                    
#r "nuget: NextFs, 0.9.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 NextFs@0.9.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=NextFs&version=0.9.0
                    
Install as a Cake Addin
#tool nuget:?package=NextFs&version=0.9.0
                    
Install as a Cake Tool

NextFs

CI NuGet Publish NuGet License: MIT

NextFs is a thin Fable binding layer for writing Next.js App Router applications in F#.

The package stays close to native Next.js concepts instead of introducing a separate framework. If you already understand how a feature works in Next.js, the goal is that you can express the same shape in F# with minimal translation.

Start Here

If you are evaluating the repository for the first time, use this path:

  1. read Quickstart
  2. read Starter app walkthrough
  3. inspect the starter example
  4. learn the wrapper rules in Directives and wrappers
  5. use Data fetching and route config for server fetch() and route segment exports
  6. use Server and client patterns for route handlers, wrappers, and mixed App Router flows
  7. use Special files for error.js, loading.js, not-found.js, and auth-interrupt conventions
  8. use API reference as the lookup table

What It Covers

  • next/link, next/image, next/script, next/form, next/head
  • next/font/local and a generated next/font/google catalog
  • App Router hooks and helpers from next/navigation
  • request helpers from next/headers
  • NextRequest, NextResponse, and route handler helpers from next/server
  • NextRequest / NextResponse constructors and init builders
  • typed server fetch() helpers for cache, next.revalidate, and next.tags
  • proxy.js config builders and NextFetchEvent
  • root instrumentation entry patterns for instrumentation.js and instrumentation-client.js
  • App Router special-file helpers and documented patterns for error.js, global-error.js, loading.js, not-found.js, global-not-found.js, template.js, default.js, forbidden.js, and unauthorized.js
  • cache invalidation and cache directives from next/cache
  • metadata, viewport, robots, sitemap, manifest, and image-metadata builders
  • ImageResponse bindings for Open Graph and icon generation
  • request/response cookie option builders
  • NavigationClient.useServerInsertedHTML, NavigationClient.unstableIsUnrecognizedActionError, LinkClient.useLinkStatus, WebVitals.useReportWebVitals, after, userAgent, forbidden, and unauthorized
  • Image.getImageProps() and action-mismatch detection for client-side server-action calls
  • inline Directive.useServer() and Directive.useCache() support
  • wrapper generation for file-level 'use client' and 'use server'

Compatibility

  • NextFs: 0.9.x
  • next: >= 15.0.0 < 17.0.0
  • react: >= 18.2.0 < 20.0.0
  • react-dom: >= 18.2.0 < 20.0.0
  • core Fable dependencies in this repo: Fable.Core 4.5.0, Feliz 2.9.0

Install

dotnet add package NextFs

Your consuming Next.js app still provides the JavaScript runtime packages:

  • next
  • react
  • react-dom

NextFs publishes Femto metadata for those packages, so a Fable consumer can check or resolve them with:

dotnet femto yourProject.fsproj
dotnet femto --resolve yourProject.fsproj

For repository work, restore local tools first:

dotnet tool restore

The repo-local tool manifest currently includes femto and fable.

Quick Start

module App.Page

open Fable.Core
open Feliz
open NextFs

[<ReactComponent>]
let NavLink() =
    Link.create [
        Link.href "/dashboard"
        prop.text "Dashboard"
    ]

[<ExportDefault>]
let Page() =
    Html.main [
        prop.children [
            Html.h1 "Hello from Fable + Next.js"
            NavLink()
        ]
    ]

Route handlers use JavaScript-shaped arguments and HTTP verb exports:

module App.Api.Posts

open Fable.Core
open Fable.Core.JsInterop
open NextFs

[<CompiledName("GET")>]
let get (request: NextRequest, ctx: RouteHandlerContext<{| slug: string |}>) =
    async {
        let! routeParams = Async.AwaitPromise ctx.``params``

        return
            ServerResponse.jsonWithInit
                (createObj [
                    "slug" ==> routeParams.slug
                    "pathname" ==> request.nextUrl.pathname
                ])
                (ResponseInit.create [
                    ResponseInit.status 200
                ])
    }
    |> Async.StartAsPromise

Server-side helpers follow the asynchronous shape of modern Next.js APIs:

module App.ServerPage

open Feliz
open NextFs

[<ExportDefault>]
let Page() =
    async {
        let! headers = Async.AwaitPromise(Server.headers())
        let userAgent = headers.get("user-agent") |> Option.defaultValue "unknown"

        return Html.pre userAgent
    }
    |> Async.StartAsPromise

Cache And Server Actions

NextFs now includes a baseline next/cache surface for App Router workflows:

  • Directive.useCache()
  • Directive.useCachePrivate()
  • Directive.useCacheRemote()
  • Cache.cacheLifeProfile
  • Cache.cacheLife
  • Cache.cacheTag / Cache.cacheTags
  • Cache.revalidatePath
  • Cache.revalidateTag
  • Cache.updateTag
  • Cache.refresh
  • Cache.noStore

Example:

let loadNavigationLabels () =
    Directive.useCache()
    Cache.cacheLifeProfile CacheProfile.Hours
    Cache.cacheTags [ "navigation"; "searches" ]

    [| "Home"; "Search"; "Docs" |]

let saveSearch (_formData: obj) =
    Directive.useServer()
    Cache.updateTag "searches"
    Cache.revalidatePath "/"
    Cache.refresh()
    ()

Metadata And Special Files

NextFs now covers the parts of App Router you need to export from layouts and metadata files:

  • Metadata
  • MetadataOpenGraph
  • MetadataTwitter
  • Viewport
  • MetadataRoute.Robots
  • MetadataRoute.SitemapEntry
  • MetadataRoute.Manifest
  • ImageMetadata
  • ImageResponse

Example:

let metadata =
    Metadata.create [
        Metadata.titleTemplate (
            MetadataTitle.create [
                MetadataTitle.defaultValue "NextFs"
                MetadataTitle.template "%s | NextFs"
            ]
        )
        Metadata.description "Next.js App Router bindings for F#."
        Metadata.openGraph (
            MetadataOpenGraph.create [
                MetadataOpenGraph.title "NextFs"
                MetadataOpenGraph.type' "website"
            ]
        )
    ]

let viewport =
    Viewport.create [
        Viewport.themeColor "#111827"
        Viewport.colorScheme "dark light"
    ]

For generated icon or Open Graph routes:

let generateImageMetadata() =
    [|
        ImageMetadata.create [
            ImageMetadata.id "small"
            ImageMetadata.contentType "image/png"
        ]
    |]

Fonts, Proxy, And Cookies

The package now also covers the main App Router surfaces that typically force people back to handwritten JavaScript:

  • Font.local
  • GoogleFont.Inter, GoogleFont.Roboto, and the rest of the generated next/font/google catalog
  • FontOptions, LocalFontSource, and FontDeclaration
  • ProxyConfig, ProxyMatcher, RouteHas, and NextFetchEvent
  • CookieOptions for request/response cookie mutation

Example:

let inter =
    GoogleFont.Inter(
        box {|
            subsets = [| "latin" |]
            display = "swap"
            variable = "--font-inter"
        |}
    )

For production App Router builds, keep `next/font` options as anonymous-record or object-literal expressions in the entry module. Next.js statically analyzes these calls and rejects helper-built objects.

let config =
    ProxyConfig.create [
        ProxyConfig.matcher "/dashboard/:path*"
    ]

More App Router Helpers

Beyond the baseline router/navigation APIs, NextFs now also includes:

  • LinkClient.useLinkStatus()
  • WebVitals.useReportWebVitals(...)
  • NavigationClient.useServerInsertedHTML(...)
  • NavigationClient.useSelectedLayoutSegmentFor(...)
  • NavigationClient.useSelectedLayoutSegmentsFor(...)
  • Navigation.unstableIsUnrecognizedActionError(...)
  • Navigation.forbidden()
  • Navigation.unauthorized()
  • Navigation.unstableRethrow(...)
  • Server.after(...)
  • Server.userAgent(...)
  • ServerFetch.fetch(...)
  • ServerFetch.fetchWithInit(...)
  • ServerRequest.createWithInit(...)
  • ServerResponse.createWithInit(...)
  • Image.getImageProps(...)

These helpers are compile-smoked in samples/NextFs.Smoke.

Server Fetch And Route Config

NextFs now includes typed helpers for Next.js server fetch() options and route segment exports:

  • ServerFetch.fetch(...)
  • ServerFetch.fetchWithInit(...)
  • ServerFetchInit
  • NextFetchOptions
  • ServerFetchCache
  • Revalidate.seconds, Revalidate.forever, Revalidate.neverCache
  • RouteRuntime
  • PreferredRegion
  • GenerateSitemapsEntry

Example:

let runtime = RouteRuntime.Edge
let preferredRegion = PreferredRegion.home
let maxDuration = 30

let loadPosts() =
    ServerFetch.fetchWithInit "https://example.com/api/posts" (
        ServerFetchInit.create [
            ServerFetchInit.cache ServerFetchCache.ForceCache
            ServerFetchInit.next (
                NextFetchOptions.create [
                    NextFetchOptions.revalidate (Revalidate.seconds 900)
                    NextFetchOptions.tags [ "posts"; "homepage" ]
                ]
            )
        ]
    )

Special Files

The repository now also documents and demonstrates the App Router special-file flow from F#:

  • error.js and global-error.js via ErrorBoundaryProps
  • template.js via TemplateProps
  • default.js via DefaultProps<'T>
  • starter patterns for loading.js, not-found.js, global-not-found.js, forbidden.js, and unauthorized.js

The dedicated guide is in Special files.

App Router Directives And Wrappers

Inline directives work for function-level cases:

  • Directive.useServer()
  • Directive.useCache()
  • Directive.useCachePrivate()
  • Directive.useCacheRemote()

File-level 'use client' and 'use server' directives are different. Fable emits imports first, so App Router entry files still need thin wrapper modules when the directive must appear at the top of the generated JavaScript file.

Generate them with:

node tools/nextfs-entry.mjs samples/nextfs.entries.json

For 'use server' wrappers, only named exports are allowed. The generator rejects default exports and export * for that case.

Repository Layout

  • src/NextFs contains the bindings package
  • samples/NextFs.Smoke contains compile-smoke coverage of the package surface
  • examples/nextfs-starter contains a minimal end-to-end App Router starter, including layout, route, instrumentation, and special-file entries generated from F#
  • tests/nextfs-entry.test.mjs covers the wrapper generator
  • tools/nextfs-entry.mjs generates directive wrapper files
  • tools/generate-google-font-bindings.mjs regenerates the GoogleFont binding catalog from official Next.js type definitions

Samples vs Examples

The repository uses samples and examples for different jobs:

  • samples are verification-oriented. They exist to compile, exercise binding shapes, and catch regressions quickly.
  • examples are usage-oriented. They are meant to look like real consumer projects someone could copy, inspect, or adapt.

In practice:

  • samples/NextFs.Smoke is a compile-smoke project for the public API surface.
  • samples/app is generated wrapper output used as a small fixture for the wrapper tool.
  • examples/nextfs-starter is a runnable App Router starter that demonstrates the intended project layout.

Examples And Docs

Validation

dotnet tool restore
dotnet femto --validate src/NextFs/NextFs.fsproj
node --test tests/*.mjs
dotnet build NextFs.slnx -v minimal
dotnet pack src/NextFs/NextFs.fsproj -c Release -o artifacts
node tools/nextfs-entry.mjs samples/nextfs.entries.json
node tools/nextfs-entry.mjs examples/nextfs-starter/nextfs.entries.json
git diff --exit-code -- examples/nextfs-starter/app examples/nextfs-starter/instrumentation.js examples/nextfs-starter/instrumentation-client.js

NuGet Publishing

The repository includes .github/workflows/publish-nuget.yml, which publishes NextFs to nuget.org on manual dispatch or tag pushes matching v*.

Publishing uses NuGet Trusted Publishing through GitHub OIDC rather than a long-lived API key.

Package page:

Contributing

Contribution workflow and commit conventions are documented in CONTRIBUTING.md.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.0 30 3/21/2026
0.8.0 29 3/21/2026
0.7.0 29 3/21/2026
0.6.0 25 3/21/2026
0.5.0 25 3/21/2026
0.4.0 29 3/21/2026
0.3.0 27 3/21/2026
0.2.0 31 3/21/2026
0.1.0 32 3/21/2026