Announcing Fable 5 Release Candidate

Mangel MaximeFebruary 27, 2026

More than a year after the first alpha release, we are happy to announce that Fable 5 is now in the release candidate stage!

You can install Fable 5 by running the following command:

# If you haven't installed a dotnet tool in this directory yet
dotnet new tool-manifest

dotnet tool install fable --prerelease

If you already have Fable installed, you can update it by running:

dotnet tool update fable --prerelease

When upgrading, make sure to upgrade your dependencies as well.

Compatibility with Fable 4

Fable 5 is compatible with Fable 4 projects, except that it now targets net10.0.

Project cracking

We removed support for the old project cracker using Buildalyzer. This has been replaced by invoking MSBuild directly, which should be more robust for the future.

WarnAsError support

It has been a long-standing request to support <TreatWarningsAsErrors>true</TreatWarningsAsErrors> in Fable, and we are happy to announce that it is now supported in Fable 5.

You can enable it in your project file like this:

<PropertyGroup>
  <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

Fable will now treat all warnings from your code as errors, but it will still allow warnings from dependencies.

This behavior should mimic how the standard F# compiler works.

.NET 10 and F# 10 support

Fable 5 adds support for the following F# features:

JavaScript / TypeScript

The JavaScript target is still the most used and stable target, and we have continued to improve it over the past year.

Support for direct nested types when using jsOptions

    let opts =
        jsOptions<Level1> (fun o ->
            o.level2.level3.valueA <- 10
            o.level2.level3.valueB <- 20
            o.topValueA <- 20
        )

generates

export const opts = {
    level2: {
        level3: {
            valueA: 10,
            valueB: 20,
        },
    },
    topValueA: 20,
};

Simplify Pojo bindings

Fable has a number of ways to create Plain Old JavaScript Objects (POJOs).

In Fable 4, people discovered a way of abusing a combination of attributes to write POJO bindings in a natural F# way.

open Fable.Core

[<AllowNullLiteral>]
[<Global>]
type Options
    [<ParamObject; Emit("$0")>]
    (
        searchTerm: string,
        ?isCaseSensitive: bool
    ) =
    member val searchTerm: string = jsNative with get, set
    member val isCaseSensitive: bool option = jsNative with get, set

let options1 = new Options("foo")

let options2 = new Options("foo", isCaseSensitive = true)

Improving on this, Fable 5 introduces a new attribute, Pojo, that can be used to write the same code with only one attribute:

open Fable.Core

[<AllowNullLiteral>]
[<JS.Pojo>]
type Options
    (
        searchTerm: string,
        ?isCaseSensitive: bool
    ) =
    member val searchTerm: string = jsNative with get, set
    member val isCaseSensitive: bool option = jsNative with get, set

let options1 = new Options("foo")

let options2 = new Options("foo", isCaseSensitive = true)

Both examples generate the same JavaScript code:

export const options1 = {
    searchTerm: "foo",
};

export const options2 = {
    searchTerm: "foo",
    isCaseSensitive: true,
};

Python

If you have been following the Fable 5 alpha releases, you know the Python target has received a staggering amount of love.

This is all thanks to Dag and the early adopters who have been testing it.

  • Python 3.12-3.14 support (3.10/3.11 are deprecated)
  • fable-library via PyPI - No more bundled runtime files
  • Modern type parameter syntax - Better type hinting in generated code
  • Py.Decorate attribute - Add Python decorators from F#
  • Py.ClassAttributes attribute - Fine-grained class generation control
  • Improved Pydantic interop - First-class support for data validation

Rust Core with PyO3

One of the biggest changes is that the core of fable-library is now written in Rust using PyO3. The motivation here is correctness, not performance:

Why Rust?

  • Correct .NET semantics - Sized/signed integers (int8, int16, int32, int64, uint8, etc.)
  • Proper overflow behavior - Matches .NET exactly
  • Fixed-size arrays - No more Python list quirks for byte streams
  • Reliable numerics - Fable 4's pure Python numerics were a constant source of bugs

fable-library via PyPI

Before Fable v5, the runtime was bundled in the NuGet package and copied to your output directory.

Now it's a simple pip/uv dependency:

# Install with pip
pip install fable-library

# Or with uv (recommended)
uv add fable-library

Rust

The Rust target kept improving as ell, and is now using Rust 2024 language edition.

Hello Erlang/BEAM !

Dag Brattli added a new target for Erlang/BEAM, which is still in its early stages.

He has been testing it using Fable.Giraffe and the first results are promising:

Benchmarks

Simple /ping endpoint returning "pong", 10,000 requests with 100 concurrent connections (oha):
MetricBEAM.NETPython
Requests/sec124,25670,3754,006
Avg latency0.79 ms1.40 ms24.9 ms
P99 latency2.49 ms3.50 ms34.2 ms

Indeed, it seems like Erlang/BEAM is a great target for Fable, and we are excited to see how it evolves in the future.

Conclusion

Fable 5 would not have been possible without the help of the many people who contributed to it in different ways, from testing, to reporting issues, to contributing code.

Thank you all for your help and support over the last year, and we hope you will continue to help us in the future to make Fable even better!