Axxes IT Consultancy

Hello world with Fable in 2019


It is with great pride that I can announce that Axxes is a partner of FableConf 2019 in Antwerp! FableConf is a community-driven event that brings people together to discuss and collaborate on the subject of compiling F# to JavaScript!
These are very exciting times for us!

Unless you don’t really know that what Fable is exactly? If that happens to be the case, this blog post is meant for you!
We are going to set up a small example from scratch and go through some of the basics. I wish to keep things as simple as possible, so keep in mind that not everything will be “production” ready at the end.

What is Fable?

Fable is a solution that lets you write JavaScript applications with F#, a functional programming language.

In essence, Fable is a compiler that lets you write F# code that compiles to JavaScript. It uses Babel under the hood and can be applied in any given situation where JavaScript can run.
Check out F# for fun and profit if you are still on the fence whether you like F# or not. Fable can give you the great benefits of functional programming combined with access to the vast JavaScript ecosystem.

Getting started with Fable

There are a few approaches to get started with Fable. Today we are going to pick the route of least configuration and compile an F# script (*.fsx) using fable-splitter.
After installing the necessary prerequisites, we will need one extra global tool for this project: Paket. Paket will download our .NET dependencies and we use this instead of NuGet to later generate a load script.

The .NET side

Install Paket with


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”install-paket.ps1″]

Now paket should be available as a cli tool. So we can initialize it in our empty folder.

paket init

This creates a new paket.dependencies file where we shall list all our required .NET dependencies.
Update it to:


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”paket.dependencies”]

and run paket install. Now that we have our packages, we still need a way to access them. What has happened so far is merely the download of some NuGet packages to our local user cache.
Using paket generate-load-scripts -t fsx we can generate a file located in the .paket\load folder.


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=””]

Once we have the .NET dependencies in place, we can create our App.fsx script that we wish to compile to JavaScript.


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”App_1.fsx”]

The JavaScript side

We will install three node modules to our project:


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”yarn.ps1″]

fable-compiler is where the magic happens, that package contains a .NET executable responsible for the compilation. fable-splitter is means of communicating with the compiler.
You can’t directly compile scripts with fable-compiler so you need a proxy so to speak.

browser-sync is optional here and it will merely provide us with an easy developers experience.
We’ll create a simple script to start a web server that watches all our files and reloads the browser when we make any changes.


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”server.js”]

The script contains some custom middleware that acts as a workaround for ES6 modules.

Next, we will need a basic index.html to host our code.


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”index_1.html”]

Lastly, let’s add some npm scripts that will trigger everything.


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”package.json”]

Initializing launch sequence

We have two moving pieces in play: the compilation and the web server.
Each can be started by running an npm script in a different terminal.

npm run compile

npm run sync

Now we can open http://localhost:3000 and we should see a log statement inside our developer tools.
Notice that the App.fsx is compiled to dist/App.js and uses ES6 modules.

Changing the App.fsx script will recompile to a new JavaScript file, that on its turn will then reload the browser.

A little exercise with Fable

To illustrate how working with Fable could be a bit different from traditional JavaScript, I found a nice sample exercise on codepen.
The premise is simple: we have a counter and if a pre-configured time limit has been reached we need to stop the clock and change its background colour.

We’ll add the clock in our index.html to somewhat match the example in the exercise.


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”index_2.html”]

When we click the button we want to parse the total amount of seconds in our input box and start a timer.


[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”App_2.fsx”]

Explicit safety from the compiler

With modern day JavaScript we can use document.querySelector to select any single element that matches a CSS selector. However, if we pass in a selector, that is just a silly string, isn’t it?
One little typo and we actually get null instead. That is why we can create a little helper function bySelector that returns an option instead.

The [<Emit>()] attribute will replace any function call to bySelector to output document.querySelector to the JavaScript bundle. Therefore the output may stay the same but the return type doesn’t!
We have sort of tricked the compiler into telling us that querySelector is unsafe, so it should return a generic option. This is great because the compiler does not let you use the value of an option directly and we can use a more concrete type than Element.
Options are great because we cannot guarantee they even exist. In other words, we are setting up a more elegant way of handling null values.

In the last match block of code we check if both the button and the input exists before we continue. If they do we wire up a click handler to the button and parse the value.
Once again the helper function parseInt returns an option of integer instead of a plain integer. Notice that we used System.Int32.TryParse, in Fable (to some extent) we can leverage our existing .NET knowledge.
Option.iter only runs the function if the option that is passed to it (using the |>) exists.

The beauty of this approach is that nothing can crash at runtime due to null pointer exception. If everything exists, our localStorage cache should be populated with some values that we will use to update the clock.

If it compiles it works!

Updating the clock

[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”App_3.fsx”]

On each interval tick, we can use the same defence mechanisms to only proceed when our current state is found (and proven valid) in the localStorage.
Only modify the clock text when the element is found. Each step along the way the compiler will protect us and we can guarantee that the access of each variable is justified.

Example of the output Fable script.

 The source

You can find the source code for this little exercise on GitHub.


  • Our current setup will not work in the older browsers right now. Fable can definitely do this but it requires some configuration. You should try this in a browser that understands ES6 modules.
  • You can always try out Fable online without installing any dependencies using the Fable repl. I chose to show you a simple configuration to get a good impression of starting from scratch.
  • For more elaborate/production ready examples, please refer to the samples in the documentation.
  • If you are a bit confused about the match syntax, checkout out this page on F# for fun and profit.
  • I’ve used yarn instead of npm, it doesn’t matter which one you use.
  • In some editors, you might get a warning that netstandard is missing. Add NETStandard.Library to your paket.dependencies and the reference to your script.
  • I’ve created a more purist functional solution as well in the monad branch. Based on the free monad recipe by Mark Seemann.


If you want to learn more about compiling F# scripts and how that came to be, come see my talk at FableConf 2019 in Antwerp!
Ticket are available now!




Image by Frans

Comments are closed.

About the author

Florian Verdonck

Florian Verdonck

.NET Consultant

Share this article


Get to know Axxes and our corporate culture!

    Keep up with news and updates in the sector