United Kingdom: +44 (0)208 088 8978

Working with React Components in F#

Writing your own F# wrappers around React components is made easy by libraries like Feliz and Fable React. Let's talk about them!

If you already consume React libraries from other developers in your Elmish apps, writing your own F# wrappers around React components might be just what you need to take the next step towards being a full stack F# developer. Luckily, it's made very easy by libraries such as Fable.React and Feliz. Following a simple set of steps, the process of writing your own wrappers will be quite straightforward.

I have been an F# developer for about a year now and this topic has always felt a bit intimidating to me. Recently I had to tackle it for a SAFE stack recipe I was writing, and I came away surprised by how easy it was. After bringing up the documentation for the React component that you wish to wrap and writing a couple of lines of interop wiring, you just need to write the corresponding F# code for each component/property you wish to access – all of which we wll cover in this blog post.

As I've mentioned in the opening paragraph, there are two libraries that we can use to write our wrappers – Fable.React and Feliz. We will go with Feliz in this blog post just because it's my favourite approach, but there are step-by-step instructions on how to write wrappers in either of these approaches in this SAFE recipe.

A simple example

First, we need to add the NPM package for the React component to our project. This is a straightforward process that you can learn about here. Having done that, we need a couple of open statements in our file to begin with, although of course you can use full qualifiers for the functions that we'll be calling instead:

open Fable.Core.JsInterop
open Feliz

Secondly, we need to call the import function from Fable.Core.JsInterop in order to reference the React component to be used in our code. You can either import it by name or using the default component, which is what the following line does. All we're doing here is referencing a React component and binding it to a keyword which in our case is numberFormat.

let numberFormat : obj = import "default" "react-number-format"

This corresponds to the following in TypeScript:

import { default as numberFormat } from 'react-number-format';

Next, we can begin to write our wrapper. We will create a type for this. At this point, we will want to have the documentation for the React library at hand since we need to model this type accordingly.

type NumberFormat =
    static member inline value (number: float option) = prop.custom ("value", number)
    static member inline thousandSeparator (char: char) = prop.custom ("thousandSeparator", char)

    static member inline input props = Interop.reactApi.createElement (numberFormat, createObj !!props)

All this code does is to let the compiler know about the properties our component can have. The first static member, for example, says that our React component might have a property named value that takes a float option. Knowing this, think about what the second member might be doing.

The third member does something a bit different, in that it actually gives us a way to call the React component in our view code and pass it the properties we've defined. See the example below.

 te

Here, we're calling the input function that we've defined as a member for the NumberFormat type. Then, we're passing it the properties that we've also defined as members for our type. As we've defined earlier, these properties take arguments – the value prop takes a float option, and the thousandSeperator prop takes a char, both of which are passed to them above.

Here's a screenshot of the result I get:

A more "useful" example

The way that we're using this number format component statically might not be very useful in real-world scenarios. We probably also want to be able to change the number dynamically. Here's what that looks like:

NumberFormat.input [
    NumberFormat.value model.Number
    NumberFormat.onValueChange (fun x -> ValueChanged x.floatValue |> dispatch)
    NumberFormat.thousandSeparator ','
]

In this function I have a field in my model named Number that has the type float option, and I'm using it as the value property for my React component. Additionally, now I have defined another member for the NumberFormat type which will allow me to use the onValueChange property of our component. I am using this member to pass in a function that will dispatch a ValueChanged message with the value that is entered into the textbox by the user.

The ValueChanged message will be received by our update function and used to set the Number field of our model to the new value. This way, the number displayed is not a hard-coded value, but rather whatever the user enters into the textbox. If you'd like to see a working example, you can visit this Git repository. It contains a stripped-down SAFE app that only has a Client project, so if you'd like to run it then all you need to do is to execute the following two commands from the root folder:

cd src/Client
npm start

Take a look at NumberFormat.fs to see the React binding, and the view function in Index.fs to see how it's used. In addition, there's a step-by-step React binding recipe in the SAFE docs to help get you going.

Thank you for reading this! I hope that you now feel confident enough to try writing your own F# wrappers around React components when needed.