Skip to content

API differences

Where the F# API differs noticeably from Solid's JavaScript API. For Solid itself, see the Solid documentation.

Option objects

Where a Solid function takes an options object, the binding usually exposes the options as optional parameters, and Fable's ParamObject attribute packs them back into an object at the call site:

[<ImportMember("solid-js"); ParamObject(1)>]
static member createMemo<'T>(compute: 'T option -> 'T, ?name: string, ?equals: EqualityFunc<'T>, ...) : Accessor<'T>

The JSX tab shows the object Fable builds from name = "doubled":

let count, setCount = createSignal 2
let doubled = createMemo ((fun (_: int option) -> count () * 2), name = "doubled")

button (onClick = fun _ -> setCount (count () + 1)) { $"doubled = {doubled ()}" }
export function Cell_c1() {
    const patternInput = createSignal(2);
    const count = patternInput[0];
    const doubled = createMemo((_arg) => ((count() * 2) | 0), {
        name: "doubled",
    });
    return <button onClick={(_arg_1) => {
            patternInput[1](count() + 1);
        }}>
        {`doubled = ${doubled()}`}
    </button>;
}

Most of these functions also have an overload that takes the options as a typed object, such as MemoOptions<'T> or EffectOptions.

Special JSX attributes

Adapted from Oxpecker.Solid. Credit to Lanayx.

Attributes that do not fit a typed constructor parameter are extension methods on the element:

MethodEmits
.attr(name, value)name={value}, for any attribute
.data(name, value)data-name="value"
.bool(name, value)name={value}
.ref(element)ref={element}
.style'(obj)style={obj}, a style object
.classObj(obj)class={obj}, a class object map
.spread(obj){...obj}
div () {
    span().data("state", "open").attr("aria-busy", false) { "data and attr " }
    span().classObj({| card = true; muted = false |}).style'({| border = "1px dashed" |}) {
        "class and style objects"
    }
}
export function Cell_c2() {
    return <div>
        <span data-state="open"
            aria-busy={false}>
            {"data and attr "}
        </span>
        <span class={{
                card: true,
                muted: false,
            }}
            style={{
                border: "1px dashed",
            }}>
            class and style objects
        </span>
    </div>;
}

style takes a string, and .style' an object; likewise class' and .classObj. Solid 1's on:, prop: and use: escape hatches (.on, .prop and .use') were removed in 3.0. See extension methods.

When you pass a variable to .ref, make it mutable:

open Fable.Core.TS

[<SolidComponent>]
let RefVariable () =
    let mutable inputRef: Dom.HTMLInputElement = JS.undefined

    div () {
        input().ref (inputRef)
        button (onClick = fun _ -> inputRef.value <- "filled via ref") { "Fill" }
    }
export function RefVariable() {
    let inputRef = void 0;
    return <div>
        <input ref={inputRef} />
        <button onClick={(_arg) => {
                inputRef.value = "filled via ref";
            }}>
            Fill
        </button>
    </div>;
}

.ref also takes a callback, .ref (fun (el: Dom.HTMLDivElement) -> ...).

Bindings that read differently in F#

  • A store setter takes an updater, never a value: Stores.
  • A signal setter takes an updater through .Invoke: Setting with the previous value.
  • An async value is a memo whose computation returns a promise, read under Loading. createResource is gone: Async memos.
  • A context is provided by calling it with the value, ThemeContext "dark" { ... }: Context.
  • lazy' takes Fable's importDynamic in place of importComponent: lazy'.

Router

@solidjs/router 2 has no JSX components: createRouter takes a tree of route definitions and returns the router to render, and links are plain a elements. createAsync is gone; read a query with createMemo. See Solid Router.

Edit this page