Skip to content

Add Support for a Third Party React Library

To use a third-party React library in a SAFE application, you need to write an F# wrapper around it. There are two ways for doing this - using Feliz or using Fable.React.

Prerequisites

This recipe uses the react-d3-speedometer NPM package for demonstration purposes. Add it to your Client before continuing.

Feliz - Setup

If you don't already have Feliz installed, add it to your client. In the Client projects Index.fs add the following snippets

open Fable.Core.JsInterop

Within the view function

Feliz.Interop.reactApi.createElement (importDefault "react-d3-speedometer", createObj [
    "minValue" ==> 0
    "maxValue" ==> 100
    "value" ==> 10
])
  • createElement from Feliz.ReactApi.IReactApi takes the component you're wrapping react-d3-speedometer, the props that component takes and creates a ReactComponent we can use in F#.
  • importDefault from Fable.Core.JsInterop is giving us access to the component and is equivalent to
import ReactSpeedometer from "react-d3-speedometer"

The reason for using importDefault is the documentation for the component uses a default export "ReactSpeedometer". Please find a list of common import statetments at the end of this recipe

As a quick check to ensure that the library is being imported and we have no typos you can console.log the following at the top within the view function

Browser.Dom.console.log("REACT-D3-IMPORT", importDefault "react-d3-speedometer")

In the console window (which can be reached by right-clicking and selecting Insepct Element) you should see some output from the above log. If nothing is being seen you may need a slightly different import statement, please refer to this recipe.

  • createObj from Fable.Core.JsInterop takes a sequence of string * obj which is a prop name and value for the component, you can find the full prop list for react-d3-speedometer here.
  • Using ==> (short hand for prop.custom) to transform the sequence into a JavaScript object
createObj [
    "minValue" ==> 0
    "maxValue" ==> 10
]

Is equivalent to

{ minValue: 0, maxValue: 10 }

That's the bare minimum needed to get going!

Next steps for Feliz

Once your component is working you may want to extract out the logic so that it can be used in multiple pages of your app. For a full detailed tutorial head over to this blog post!

Fable.React - Setup

1. Create a new file

Create an empty file named ReactSpeedometer.fs in the Client project above Index.fs and insert the following statements at the beginning of the file.

module ReactSpeedometer

open Fable.Core
open Fable.Core.JsInterop
open Fable.React

2. Define the Props

Prop represents the props of the React component. In this recipe, we're using the props listed here for react-d3-speedometer. We model them in Fable.React using a discriminated union.

type Prop =
    | Value of int
    | MinValue of int
    | MaxValue of int 
    | StartColor of string

One difference to note is that we use PascalCase rather than camelCase.

Note that we can model any props here, both simple values and "event handler"-style ones.

3. Write the Component

Add the following function to the file. Note that the last argument passed into the ofImport function is a list of ReactElements to be used as children of the react component. In this case, we are passing an empty list since the component doesn't have children.

let reactSpeedometer (props : Prop list) : ReactElement =
    let propsObject = keyValueList CaseRules.LowerFirst props // converts Props to JS object
    ofImport "default" "react-d3-speedometer" propsObject [] // import the default function/object from react-d3-speedometer

4. Use the Component

With all these in place, you can use the React element in your client like so:

open ReactSpeedometer

reactSpeedometer [
    Prop.Value 10 // Since Value is already decalred in HTMLAttr you can use Prop.Value to tell the F# compiler its of type Prop and not HTMLAttr
    MaxValue 100
    MinValue 0 
    StartColor "red"
    ]