Skip to content

F# Guide — Fable: Compiling F# to JavaScript

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about F# Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Fable is a compiler that translates F# code to JavaScript, enabling .NET developers to write browser applications with F#'s type system, with seamless React integration through Elmish.

What You'll Learn

  • Setting up Fable projects
  • F# to JavaScript compilation
  • React integration with Elmish
  • Interop with JavaScript libraries
  • Bundle optimization

Why It Matters

Fable lets F# developers target the browser without learning JavaScript. Shared types between server and client reduce duplication. Doda Browser uses Fable for extension development.

Real-World Use

Full-stack F# applications with SAFE stack (Saturn, Azure, Fable, Elmish), React dashboards, and Node.js services.

flowchart LR
    A["Fable"] --> B["Setup"]
    B --> C["Compilation"]
    C --> D["Elmish"]
    D --> E["Deployment"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Project Setup

# Install Fable template
dotnet new install Fable.Template
dotnet new fable -n MyApp
cd MyApp
npm install
npm start

Basic F# to JavaScript

module App

open Browser
open Browser.Dom

let greet name =
    document.getElementById("root").innerText <- sprintf "Hello, %s!" name

greet "Fable"

Elmish Architecture

module App

open Elmish
open Elmish.React

type Model = { Count: int }

type Msg =
    | Increment
    | Decrement
    | Reset

let init() = { Count = 0 }

let update msg model =
    match msg with
    | Increment -> { model with Count = model.Count + 1 }
    | Decrement -> { model with Count = model.Count - 1 }
    | Reset -> { model with Count = 0 }

let view model dispatch =
    Html.div [
        Html.h1 [ prop.text (sprintf "Count: %d" model.Count) ]
        Html.button [ prop.text "+"; prop.onClick (fun _ -> dispatch Increment) ]
        Html.button [ prop.text "-"; prop.onClick (fun _ -> dispatch Decrement) ]
        Html.button [ prop.text "Reset"; prop.onClick (fun _ -> dispatch Reset) ]
    ]

Program.mkSimple init update view
|> Program.withReactSynchronous "root"
|> Program.run

JavaScript Interop

open Fable.Core
open Fable.Core.JsInterop

// Import JavaScript module
[<Import("default", "moment")>]
let moment: obj = jsNative

// Call JavaScript function
[<Emit("console.log($0)")>]
let consoleLog (x: obj) : unit = jsNative

// Use JavaScript object
let date = moment?().format("YYYY-MM-DD")
consoleLog date

Promises

open Fable.Core.JS

let fetchData url = promise {
    let! response = fetch url []
    let! json = response.text()
    return json
}

// Usage
let loadData() = promise {
    let! data = fetchData "http://api.example.com/data"
    console.log(data)
}

Common Mistakes

1. Using .NET-specific APIs

Not all .NET APIs are available. Browser APIs replace System.IO, System.Net, etc.

2. Forgetting JS interop patterns

Use [<Import>], [<Emit>], and jsNative for JavaScript interop.

3. Large bundle sizes

Fable generates optimized JS but be mindful of imports. Use tree-shaking.

4. Async confusion

Fable uses JavaScript Promises. Use promise { } instead of async { }.

5. Type mismatches

JavaScript types are dynamic at runtime. Use Fable's type mapping for correct interop.

Practice Questions

1. What is Fable? A compiler that translates F# code to JavaScript for browser and Node.js targets.

2. What is Elmish? A UI framework for Fable implementing the Model-View-Update architecture, similar to Elm.

3. How do you call JavaScript from Fable? Use [<Import>], [<Emit>], or the jsNative placeholder for JS interop.

Challenge: Build a simple counter application with Fable and Elmish.

FAQ

{{< faq question="Is Fable production-ready?" >} Yes. Fable v4+ is stable and used in production. Many companies use Fable for their frontend applications. {{< /faq >}}

{{< faq question="Can I use React components from Fable?" >} Yes. Elmish.React renders React components. You can also use Feliz for a more React-like syntax. {{< /faq >}}

{{< faq question="How does Fable compare to TypeScript?" >} Fable uses F#'s type system which is more expressive than TypeScript. It supports discriminated unions, type providers, and computation expressions. {{< /faq >}}

{{< faq question="Can I use npm packages with Fable?" >} Yes. Fable projects use npm for JavaScript dependencies. Use [<Import>] to import JS modules. {{< /faq >}}

{{< faq question="What is the SAFE stack?" >} A full-stack F# framework: Saturn (server), Azure (cloud), Fable (client), Elmish (UI). All in F#. {{< /faq >}}

Mini Project

Build a simple counter with Fable and Elmish:

module Counter

open Elmish
open Elmish.React

type Model = { Value: int }

type Msg = Increment | Decrement

let init() = { Value = 0 }

let update msg model =
    match msg with
    | Increment -> { model with Value = model.Value + 1 }
    | Decrement -> { model with Value = model.Value - 1 }

let view model dispatch =
    Html.div [
        Html.h1 [ prop.text (string model.Value) ]
        Html.button [ prop.text "+"; prop.onClick (fun _ -> dispatch Increment) ]
        Html.button [ prop.text "-"; prop.onClick (fun _ -> dispatch Decrement) ]
    ]

Program.mkSimple init update view
|> Program.withReactSynchronous "app"
|> Program.run

What's Next

Now that you understand Fable, explore Paket for .NET package management.

Topic Description Link
F# Paket Package management {{< ref "29-paket" >}}
F# Common Libraries Ecosystem overview {{< ref "30-common-libraries" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro