United Kingdom: +44 (0)208 088 8978

Creating Fable forms with Fable.Form

Matt takes a look at Fable.Form, a collection of libraries for defining and rendering forms in Fable apps.

We're hiring Software Developers

Click here to find out more

As its name suggests, Fable.Form allows you to create forms in your Fable application. It is a collection of Fable libraries, of which you can use all or only a subset:

  • Fable.Form, which contains base types without details of any particular field types.
  • Fable.Form.Simple, which contains support for a few different field types such as text input, checkboxes etc.
  • Fable.Form.Simple.Bulma, which uses Bulma to render the forms defined using Fable.Form.Simple.

The advantage of the split is that it is possible to create F# libraries that use different CSS libraries for rendering, such as Fable.Form.Simple.MaterialUI, or ones that support different field types than Fable.Form.Simple.

The docs state that Fable.Form allows you to build forms that are composable, type safe, scalable, terse and modular. These are intriguing claims, and there are plenty of examples, which are worth exploring. The examples are so good that I'll just highlight one of them here rather than create my own 🙂

Basic example

Suppose you have an email address type and a corresponding function that validates that a string can make a valid email address:

module EmailAddress

type T =
    private EmailAddress of string

...

let tryParse (text : string) =
    if text.Contains("@") then
        Ok (EmailAddress text)
    else

        Error "The e-mail address must contain a '@' symbol"

You can use the parsing function in your Fable.Form definition, and have the form use the underlying type when dispatching Elmish messages:

type Values =
    {
        Email : string
        Password : string
        RememberMe : bool
    }

type Msg =
    | FormChanged of Model
    | LogIn of EmailAddress.T * string * bool

let init () = ...

let update (msg : Msg) (model : Model) = ...
    match msg with
    | FormChanged newModel -> ...
    | LogIn (_email, _password, _rememberMe) -> ...

let form : Form.Form<Values, Msg, _> =
    let emailField =
        Form.textField
            {
                Parser =
                    EmailAddress.tryParse
                Value =
                    fun values -> values.Email
                Update =
                    fun newValue values ->
                        { values with Email = newValue }
                Error =
                    fun _ -> None
                Attributes =
                    {
                        Label = "Email"
                        Placeholder = "some@email.com"
                        HtmlAttributes = [
                            prop.autoComplete "email"
                        ]
                    }
            }

    let passwordField = ...

    let rememberMe = ...

    let onSubmit =
        fun email password rememberMe ->
            LogIn (email, password, rememberMe)

    Form.succeed onSubmit
        |> Form.append emailField
        |> Form.append passwordField
        |> Form.append rememberMe

let view (model : Model) (dispatch : Dispatch<Msg>) =
    Form.View.asHtml
        {
            Dispatch = dispatch
            OnChange = FormChanged
            Action = Form.View.Action.SubmitOnly "Sign in"
            Validation = Form.View.ValidateOnSubmit
        }
        form
        model

With the form wired up as above, validation is taken care of for you:

Validation error shown against email field whose entry has no '@' sign.

As promised, you can also compose forms:

Code snippet and live demo showing composition in Fable.Form.

Wrap up

That was just a quick post to give you a flavour of Fable.Form. I'm impressed by its clean interface, and it seems to me that it delivers on its promises of being composable, type safe, scalable, terse and modular. In a future post I'll show how to use Fable.Form in a SAFE app!

Thanks to Maxime and all Fable.Form contributors for making this library available!