Welcome back! This article is the part of “Getting started with Fable” series, so if you missed the previous articles please visit:
The idea of these series is to create a template which can be used as a starting point for enterprise application of any complexity. So as I’d love to see a functional application with routing, state management, unit tests, end-to-end tests, all possible linting tools, code style checkers, environment dependant configuration, build scripts (I hope I didn’t forget anything important).
The main topic of today’s post is application’s routing. I wanted to integrate React Router to the Fable-based application and as far as you are reading this post I’ve succeeded. And yes, I know that there is a Elmish Router which can be used in Fable applications. But there are couple of reasons why I’m not using it:
- It’s an out-of-the-box solution. So it’s just working. It’s too simple, no challenge;
- Most of the front-end engineers are not aware about Elm architecture for front-end applications. The most important part of my “investigation” is whether it’s possible to build application which is readable/maintainable by both .NET and front-end developers and at the same time taking advantage of functional programming as a main paradigm;
- Integrating a third-party React component is in general very important exercise. Nowadays, it’s mostly impossible to develop big enterprise applications without consuming a components library or several independent components (like grid, drag-and-drop lists, etc.).
So yes, I’m aware about Elm architecture but for this series I’ll avoid it.
There are couple of changes in the solution since the last post. And the first thing is integration with GitHub actions. Now
Yarn is available as a part of the default image, hence you must remove a couple of lines from the configuration file as in the screenshot below. Otherwise you’ll experience build failures with messages saying that “yarn is already installed”:
Just a small quality of life change from the GitHub Actions side. I’m not sure about the current state of
NPM but a year ago when I was responsible for configuring the CI environment for my Angular project I faced couple a of issues with
NPM which where resolved by migrating to
Yarn. That’s why on all new projects I’m starting I prefer using
Yarn instead of
One last word regarding
Yarn. To keep all your dependencies up to date you can use an interactive console, which is very handy. Just type:
yarn upgrade-interactive --latest
Yarn will provide you a way to easily select the packages you want to upgrade (
--latest parameter can be omitted in case you want to upgrade packages using version patterns provided in
Before start with the main dish let’s make one more improvement to our solution. It’s really hard to imagine a web development project without a development server. So let’s get one set up:
yarn add webpack-dev-server --dev
After installing the dependency you need to add a new script to the
package.json file like on the screenshot below (in this and further articles I’ll post some minor code changes as screenshots to emphasize change itself):
The next step is to configure this package:
There are three properties we want to configure right now. The first one is
contentBase. It’s responsible for configuring the output directory which will be used as a root for the web server. The second one,
open, is not required and can be omitted. This is just a small automation script, which will open our application in the specified browser (in my case – Google Chrome). Please, take into account that it’s not recommended to commit this property, because it’s OS dependant (technical limitation), so if you have a team members which are using different OS (MacOS, Linux, Windows) it’s very likely that some of them will have issues with it. And the last but not least is
historyApiFallback. This one is mandatory for client-side routing in Single Page Applications. So in case when you have only one
index.html page and all other routes make sense only on the client (
/admin/sessions, etc.) you want the server to always return that single page. That’s where this property comes into play.
Now all you need is to run command:
and application will be opened in the browser of choice (by default application will be served at
Here we come. Now when we are finished with all the preparations we can start developing the application itself (finally). Most of the modern applications are Single Page Application, it mean that we need a routing to display different pages on the client side without actual navigation.
At first, we need to install the React Router (it’s the most popular routing module for the React applications, I see no reason to use something else):
yarn add react-router-dom --dev
And now it’s the place where thing getting more interesting. I need to warn you that I’m not an expert in
F# (not even close), I’m just a person who is trying to learn by doing (making mistakes is included). So, what I did:
- There is a manual in the Fable React repository (link). I used it as a starting point.
- For the initial showcase I needed a couple of components from the React Router: BrowserRouter, Link, Switch, Route. I’ve created a file for each of the classes. As for me, the process looks similar to writing a type definition for libraries in
TypeScript(that’s not a surprise, because actually it was a sort of type definition but for
- Then I’ve declared discriminated unions for each set of attributes (parameters) and functions for each component.
Most of the code doesn’t look like rocket science, but there is an interesting code related to
F# there is no way to define such type that’s why
Fable provides a generic type U* (U2, U3, etc.) to make it work. You can see the usage of this type in the
And now the only thing left is to actually use these definitions. For this purpose let’s modify the App component to create two stub pages with different welcome messages:
The only thing I’d like to mention is the
!^ operator – it’s the Fable’s type casting operator, it’s required to work with U* generic type. This operator is a sort of syntax sugar for the alternative version which is not so convenient to use: U3.Case*. Case1 means first type from the generic type declaration.
Now you can start the application and see that it’s working!
That’s basically it for today. In the next posts I’ll cover even more development topics, so please stay tuned. If you have any questions or faced any issues during following this manual don’t hesitate to write in the comments below.
Thanks a lot for reading! I hope you enjoyed. If you find this material useful, don’t forget to subscribe and share with your colleagues and friends! Thanks!