Open-source project
fable-hub/Feliz avatar
fable-hub/Feliz

Feliz: an F# take on the React API that keeps HTML, props, and CSS discoverable

A fresh retake of the React API in Fable and a collection of high-quality components to build React applications in F#, optimized for happiness

613 stars94 forksF#MIT

At a glance

What is it?
Feliz is a Fable binding that exposes the React API to F# with typed HTML elements, `prop` attributes, and `style` properties instead of stringly typed markup. It ships as a NuGet package, works with Fable 4 and React 19, and can host Elmish state machines inside React components.
Who is it for?
Feliz fits an F# team that wants React without writing markup in strings, and that values IntelliSense over `Margin of obj` in a component tree. It is the wrong tool for a JavaScript shop, since the whole value is in F# type inference, and it is the wrong tool if you want a full framework with routing, state management, and components included, because Feliz is a binding layer rather than an application framework.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly F#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem is that React in F# turns markup into strings

Fable compiles F# to JavaScript, so you can write React components in F#, but the raw React API is not a comfortable fit for a functional language. Feliz describes itself as a fresh retake of the React API in Fable, optimized for happiness, and a collection of high quality components for building React applications in F#. The problem it addresses is the shape of the React API itself. Components are functions returning elements, elements are tagged objects with string properties, and a large part of the API surface is attributes, CSS properties, and hook arguments, all of which are strings or loosely typed objects in JavaScript. Feliz wraps that surface in F# types and a syntax where the compiler helps you find the right function name. The audience is F# developers who want to keep the language's benefits while building a React frontend, which in practice means teams with a .NET backend and a browser UI written in the same language.

Html, prop, and style are three separate namespaces on purpose

The design decision that makes Feliz feel different from writing JSX by hand is that the three concerns are separated. `Html` gives you the elements, so `Html.div`, `Html.button`, `Html.h1`. `prop` gives you attributes on an element, so `prop.onClick`, `prop.text`, `prop.style`. `style` gives you the CSS properties themselves, so `style.marginRight 5`. The README frames the benefit as discoverable attributes with no more functions, so `Html` attributes and CSS properties are globally available and easy to find, which removes the guesswork of looking up the exact casing of an HTML attribute. The F# compiler does the rest, because the element and attribute names are union cases rather than free-form strings. The README also claims proper documentation for each attribute and CSS property, and the published docs site carries live examples for the whole surface.

A counter component is the whole quick example

The README opens with a component that shows the shape of the library end to end:

fsharp
module App

open Feliz

[<ReactComponent>]
let Counter() =
    let (count, setCount) = React.useState(0)
    Html.div [
        Html.button [
            prop.style [ style.marginRight 5 ]
            prop.onClick (fun _ -> setCount(count + 1))
            prop.text "Increment"
        ]

        Html.button [
            prop.style [ style.marginLeft 5 ]
            prop.onClick (fun _ -> setCount(count - 1))
            prop.text "Decrement"
        ]

        Html.h1 count
    ]

Three things stand out. The `[<ReactComponent>]` attribute turns a plain function into a React component, so the compiler plugin handles the conversion rather than a hand-written call. State comes from `React.useState(0)` in the familiar tuple form, which is Fable's mapping of the JavaScript hook. And the element tree is a nested list, with attributes applied through a computation expression, which is where F#'s list syntax earns its keep. Mounting is then two lines of Fable:

fsharp
open Browser.Dom

let root = ReactDOM.createRoot (document.getElementById "root")
root.render (Counter())

Style properties are overloaded functions, not an object bag

The README calls out type safety as the headline feature and describes it as no more `Margin of obj` but instead a plethora of overloaded functions to account for the overloaded nature of CSS attributes, covering 90%+ of CSS styles, values, and properties. The difference is concrete. A JSX style attribute is an object whose values may be strings, numbers, or units, and nothing catches a typo in a property name. In Feliz, `style.marginRight 5` resolves to an overloaded function that knows the allowed types for that property, so a wrong unit or an unsupported value fails at the call site rather than silently producing nothing in the browser. The tradeoff is that CSS support is broad rather than exhaustive, and the pattern of overloads means the compiler is doing a lot of work to pick the right variant. It also explains why Feliz leans on erased function bodies for size: the README claims approximately zero bundle size increase where everything function body is erased from the generated JavaScript unless you actually use said function.

React.useElmish lets Elmish state machines live inside a React component

F# web developers often arrive from Elmish, with its model-view-update architecture, and Feliz provides a bridge rather than making you choose. The `React.useElmish` hook integrates Elmish state machines with React components, and it lives in a separate package, `Feliz.UseElmish`. The example in the README shows a message type, a state record, an `init` function returning state and command, and an `update` function matching on the message:

fsharp
type Msg =
    | Increment
    | Decrement

type State = { Count : int }

let init() = { Count = 0 }, Cmd.none

let update msg state =
    match msg with
    | Increment -> { state with Count = state.Count + 1 }, Cmd.none
    | Decrement -> { state with Count = state.Count - 1 }, Cmd.none

and then a component that pulls the state and dispatch out of the hook:

fsharp
        let state, dispatch = React.useElmish(init, update, [| |])
        Html.div [
            Html.h1 state.Count
            Html.button [
                prop.text "Increment"
                prop.onClick (fun _ -> dispatch Increment)
            ]

This is the model-view-update loop living inside a React rendering model, which is a real bridge rather than a coincidence, and it is why the hook lives in its own package rather than in the core. Feliz is also stated to be compatible with Femto, another F# view library.

Getting started is a dotnet template plus npm install

The quick start is a .NET template that wires up Fable, webpack, and the Feliz packages for you:

bash
# install the template (if you haven't already)
dotnet new install Feliz.Template
# create a new Feliz project from the installed template
dotnet new feliz -n MyProject
# navigate to the created project folder
cd MyProject
# install the npm dependencies
npm install
# install .NET tools
dotnet tool restore
# start the development server
npm start

The order matters. `dotnet new install Feliz.Template` registers the template with the .NET SDK, `dotnet new feliz` creates a project named MyProject, `npm install` fetches the JavaScript side, `dotnet tool restore` restores the Fable toolchain, and `npm start` runs the development server. The template route is much better than assembling a Fable project by hand, because the Fable, webpack, and Feliz configuration have to agree with each other and the template guarantees they do. The repository also ships a `playground` directory and a `start:play` script that runs the playground's own dev server, which is the fastest way to see what a change looks like.

The repository is a workspace of many Feliz projects

This is not a single package repository. The root `package.json` is a private npm workspace named `feliz-packages` whose workspaces cover `src/*`, `tests/*`, and `docs/`, excluding a test counter package, and the repository carries two solutions, `Feliz.sln` and `Feliz.Pack.sln`, the second presumably for the packaging pipeline. The `src` directory holds the individual Feliz projects, and the README notes that each project in Feliz has its own changelog file in the `src` folder, linking directly to `src/Feliz/CHANGELOG.md` for the core package. Versioning is therefore per package rather than per repository, which is why the NuGet badge is the meaningful version indicator and why the repository has no GitHub releases at all. For consumers, that means you take a NuGet package version for each add-on you use, and you track changes per changelog file rather than through one repository release feed.

MIT licensed, docs-driven, and pinned to a modern React

The licence is MIT, with the LICENSE file at the repository root, and the project targets a current stack: the root `package.json` depends on `react` and `react-dom` at 19.1.0, uses Vite with the React plugin for the docs and playground, and runs tests with Vitest and the Testing Library packages including `user-event`. The build scripts show the Fable pipeline clearly, with `dotnet fable watch` for development and `dotnet fable docs` for a production bundle, both excluding a `Feliz.CompilerPlugins` project and passing through to webpack. The last push was on 2026-09-25, and the project is documented through a dedicated site with a Feliz syntax reference, a guides section, and an ecosystem directory listing community libraries and official add-ons for UI, components, hooks, testing, and tooling. The ecosystem page is the honest place to look if you are deciding whether Feliz has the pieces you need, since Feliz itself is deliberately a binding layer and not a component library with everything in it.

Editorial conclusion

Feliz fits an F# team that wants React without writing markup in strings, and that values IntelliSense over `Margin of obj` in a component tree. It is the wrong tool for a JavaScript shop, since the whole value is in F# type inference, and it is the wrong tool if you want a full framework with routing, state management, and components included, because Feliz is a binding layer rather than an application framework. Before adopting it, install `Feliz.Template` and run the counter example end to end, then check the changelog in `src/Feliz/CHANGELOG.md` because the package is versioned per project and the repository itself has no GitHub releases to pin against.

Frequently asked questions

How do I create a new Feliz project?

Install the template with `dotnet new install Feliz.Template`, create a project with `dotnet new feliz -n MyProject`, then run `npm install`, `dotnet tool restore`, and `npm start` inside the new folder to start the development server.

How does Feliz handle CSS styles compared to JSX?

Instead of a loosely typed style object, Feliz exposes overloaded functions for CSS properties, so `style.marginRight 5` resolves at the call site. The README describes this as covering 90% or more of CSS styles, values, and properties, with overloads accounting for the varied types a property accepts.

Can I use Elmish state management with Feliz?

Yes. The `React.useElmish` hook integrates Elmish state machines with React components and is delivered through the separate `Feliz.UseElmish` NuGet package, which you open with `open Feliz.UseElmish`.

Which React version does Feliz target?

The repository's package.json depends on `react` and `react-dom` at version 19.1.0. Feliz aims to support the full React API for building components with hooks, context, and more.

Where can I find the list of Feliz packages and add-ons?

The documentation site has an ecosystem section listing community libraries and official add-ons covering UI, components, hooks, testing, and tooling. Each project also keeps its own changelog file in the `src` folder, with the core changelog at `src/Feliz/CHANGELOG.md`.

Official sources

  1. fable-hub/Feliz on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fable-hub-feliz.svg)](https://hysenlabs.com/projects/fable-hub-feliz)

Was this useful?