Getting started with Fable. Scaffolding

Photo by Ricardo Gomez Angel on Unsplash

This article is the continuation of the “Getting started with Fable” series, so if you missed the previous one please visit .

So to briefly recall in the first article we’ve set up the development environment to start the Fable/React project. Now it’s time to prepare a minimal compilable project, which often called a scaffold. The reason I’m writing this is the fact that in my personal opinion doesn’t provide enough details and explanations. It has enough samples but lacks explanations. So I’ll try to fill this gap. This guide is written by the front-end developer, so take in mind that I can assume some things (which are common in front-end development) and give them without detailed explanation. I’ll try to avoid such things, because the idea is to prepare a guide for any developer without any specific background. But if you find something unclear feel free to leave a comment I’ll update the articles with required explanations.

Let’s start. At first you of course need to create a working directory with the name of the project. In that folder you need to initialize the version control system (in my case it’s git and I hope in your case it will be git also). Then you need to initialize the package.json file with the help of yarn (or NPM). You can choose any of these tools, it will not affect the development process significantly. There are a lot of small differences in caching, console syntax, etc. But the end result is quite similar.

# Initializing git
git init
# Initializing package.json
yarn init
Initializing version control and package.json

If you’ve never worked with Yarn/NPM before it’s not a big deal. These are the package managers in the Node.js world. By using these tools you’ll be able to download and reference millions of packages which can significantly speed up the project’s development. Let me guide you through the most important things you should know about package management in your application:

  • All dependencies and their versions are listed in the package.json file. However in most cases this file only contains MINIMAL version and the rule (e.g. “^8.0.0” means that any version of package can be used if the major version of the package equals 8);
  • Resolved package tree with EXACT versions for each package and all its dependencies is listed in the package-lock.json (for NPM) or yarn.lock (for Yarn) files. These files are crucial for team work, continuous integration and continuous delivery pipelines, because they help you to guarantee that all developers and CI machines downloaded the same list of packages. These files MUST be committed to version control system;
  • All the dependencies are stored in the node_modules directory. THIS DIRECTORY MUST NOT BE COMMITTED TO THE VERSION CONTROL SYSTEM. I hope that for most of you this statement is obvious, however I’ve seen so many enterprise projects where this wasn’t true. So I’m mentioning this just one more time;
  • Both Yarn and NPM, allow you to install the global packages, but you should try to avoid it, because it’s hard to keep such kind of dependencies in sync with local packages (I had very negative personal experience with @angular/cli installed globally) and it’s mostly impossible to manage such global dependencies on the team level;
  • There are two sections for dependencies in the package.json file: dependencies and devDependencies. If you are not developing the public package which is going to be part of NPM it’s not super important to separate dependencies between these two groups. However it is still important to understand, especially if you are developing back-end using Node.js. In that case you probably don’t want to install development dependencies on production machines. As a rule of thumb you can ask yourself a question “Is this dependency used only for development purpose?” and if the answer is “yes” then it should be a part of devDependencies. Good examples of pure development dependencies could be CLI tools, transpilers and unit testing/e2e testing frameworks.

As the next step you’ll need to create an F# project for the Fable. For all .NET developers it’s a very straight-forward action. But for most front-end developers this is a buzzword. So I’ll add some explanations: to compile a .NET code you need to list all the files in the project files. In C# language the order of files in the project file doesn’t really matter but in F# language it does matter, because it behaves similar to JavaScript and executes code immediately like any other scripting language. So for example if you have file where you use some dependency in the project file this dependency should be listed before the file that is actually using it.

This is probably not very “in depth” explanations, but I hope that it should be enough. So let’s move on to the code snippet itself:

# Creating F# project
dotnet new classlib -lang f# -n app -o src
# Rename Library.fs => app.fs
cd src
dotnet add package Fable.Core
dotnet add package Fable.Browser.Dom
dotnet add package Fable.React
Creating Fable project

By default when creating the class library CLI creates a file Library.fs which is not a self-descriptive name (probably I didn’t find the proper argument, please, add a comment if I missed something, it will be very useful for people who are using this guide), so we will rename it to main.fs and we will do so in the project file also.

Now we need to setup the webpack which is the most used tool for bundling assets in the front-end world. As the first step we need to install all the dependencies and then I’ll explain what actually we did:

# Installing Webpack
yarn add webpack –dev
yarn add webpack-cli –dev
yarn add clean-webpack-plugin –dev
yarn add html-webpack-plugin –dev
yarn add fable-loader –dev
yarn add fable-compiler –dev
yarn add @babel/core –dev
# Installing other dependencies
yarn add react
yarn add react-dom
Installing webpack dependencies

Now let’s briefly walk through all the dependencies:

  • webpack and webpack-cli are the bundler itself and the CLI. If you need more details regarding the tool you can visit official documentation;
  • clean-webpack-plugin is the small plugin which will clean up the output directory before each build;
  • html-webpack-plugin is the important plugin which will generate initial HTML file for our project. In our cases we will add a simple template, but in some cases it’s enough to have an empty HTML. This plugin is inserting all the bundled assets in the HTML itself without the need to update index.html file manually. If you have a question in you mind “Why HTML would change so often so I need to automate this process?” I can bring you the simplest sample: it is a good practice to add an MD5 hash sum to the name of each bundle for proper client-side caching. There are more use cases of course, but this one is the most common;
  • @babel/core, fable-compiler, fable-loader are the transpilers/compilers which will basically transform your fancy F# into JavaScript bundle for browsers;
  • react and react-dom are the libraries for front-end development. Currently React is one the most popular libraries for front-end development along with Angular (framework) and Vue.js (framework). If you are not familiar with the React you can read the official documentation which is super cool and contains a lot of information from the basics to the advanced topics: link;

Now lets create a webpack.config.js file in the root directory of our project (not in the src):

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.fsproj',
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Fable-React-App',
template: './src/index.html'
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
module: {
rules: [{
test: /\.fs(x|proj)?$/,
use: 'fable-loader'

I doubt that I need to give some detailed explanations. Basically here in one file we just enable all previously downloaded dependencies and put the outcome of the build into dist directory.

Alright, we are nearly there. Now we need to write the “hello world!” application and we are done with this part. Let’s start from the HTML template which will be used by wepback to produce the real index.html file:

<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
<div id="root"></div>
view raw index.html hosted with ❤ by GitHub

Basically the only difference of this template from the default index.html file is that we’ve added a default container for our React application: div#root.

Now we need to write the React application itself. For this purpose I’ll try to follow regular front-end naming convention and put each component into separate file. So in the “hello world!” application we will have 2 F# files: main.fs (contains application component creation and attaching it to Document Object Model (DOM)) and app.fs (component itself):

// main.fs
module Main
open Fable.React
open App
let document = Browser.Dom.window.document
let root = document.getElementById "root"
ReactDom.render(App(), root)
// app.fs
module App
open Fable.React
let App() =
div [] [ str "Hello from Fable-React Application!"]
main.fs and app.fs

Most of the code is similar to an official “hello world!” sample from React documentation. However, there is an important thing here I want to highlight, because if you are not very familiar with F# you can probably waste some time on this. You can see two types of function call in this code snippet. The regular function calls without parentheses and more C#/JavaScript call with the parentheses. The thing is that both of these calls are not using parentheses but the function call with parentheses actually pass special data structure as a parameter – tuple. I’m not the right person to explain why there is such a difference. But my understanding is that all “native” F# functions support partial application, so by “calling” a function sum with only one parameter you are actually creating another function which accepts only one parameter. And this behaviour is supported by all the F# function out of the box. However it’s quite hard to provide such functionality for external APIs, especially in JavaScript where you can omit any number of parameters. That’s why most (if not all) external APIs accept only one parameter as a tuple.

Congratulations! You did it. To compile your application you need to use command yarn webpack and you’ll find your new Fable Hello World application in the dist directory.

The last thing I want to leave here is the .gitignore configuration we should have at this stage:

# VS Code
# .NET
# Application
view raw .gitignore hosted with ❤ by GitHub

That’s basically it for today. In the next posts I’ll try to cover topics like development environment setup (build on change and development server) and first components/services. If you have any questions or faced any issues during following this manual don’t hesitate to write in the comments below.

Also if you find this material useful, don’t forget to subscribe and share it with your colleagues! Thanks!

7 thoughts on “Getting started with Fable. Scaffolding

  1. Interesting articles, I promise to fully read them soon, but at first glance I’m under the impression that getting started with Fable is quite harder than getting started with WebSharper and also that there are more dependencies on JavaScript ecosystem. Maybe it’s simply because I’m already using WebSharper (with great satisfaction). Anyway thank you again for sharing, for sure it’s worth reading in depth and… trying it out myself asap…

    Liked by 1 person

    1. Thanks a lot for feedback. Regarding your impression I kind of agree. In my opinion Fable is more like a transpiler. It doesn’t go with the infrastructure and mostly relies on the existing front-end tools like Webpack and Babel. It simplifies the adoption by front-end developers, but may slow down adoption by .NET developers.

      Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s