Skip to content

Oxpecker DSL

Partas.Solid is a fork of Oxpecker.Solid and keeps its DSL. Attributes go in a constructor call, children go in a computation expression after it:

tag (attribute = value, ...) {
    child
    child
}

Elements

Each HTML element is a class, and its attributes are optional constructor parameters. Attribute names that are F# keywords get an apostrophe: class', type', for', when'. An attribute with a fixed set of values takes a case of a union in Attr, and the JSX gets its string:

button (class' = "btn", type' = Attr.ButtonType.Button, title = "A plain button") { "Button" }
export function Cell_c1() {
    return <button class="btn"
        type="button"
        title="A plain button">
        Button
    </button>;
}

Event handlers receive the event type of their event. onClick takes a Dom.MouseEvent -> unit, onInput a Dom.InputEvent -> unit; Dom is in Fable.Core.TS.

Children

The computation expression after an element holds its children: strings, numbers and other elements, in any mix.

div () {
    "Button label"
    button () { "Button" }
    span () { "Count:" }
    5
}
export function Cell_c2() {
    return <div>
        Button label
        <button>
            Button
        </button>
        <span>
            Count:
        </span>
        {5}
    </div>;
}

An element with no children does not need the braces: input (type' = "checkbox"). Void elements such as input, br and img do not accept any.

Ordinary F# works around and inside the block. An if becomes a ternary in the JSX:

let name = "Solid"

div () {
    p () { $"Hello, {name}!" }
    if name.Length > 3 then
        p () { "That is a long name." }
}
export function Cell_c3() {
    return <div>
        <p>
            {concat("Hello, ", "Solid", "!")}
        </p>
        {("Solid".length > 3) ? (<p>
            That is a long name.
        </p>) : (undefined)}
    </div>;
}

Embedded JSX

JSX.Element collides with HtmlElement in the F# compiler, so the builder does not take Fable's JSX.jsx directly. unbox it. JSX.jsx only takes a string literal:

div () {
    unbox<HtmlElement> (JSX.jsx "<button>Raw JSX button</button>")
}
export function Cell_c4() {
    return <div>
        <button>Raw JSX button</button>
    </div>;
}

Lambda children

Some components, such as For, take a function as their child instead of elements.

ul () {
    For.Keyed(each = [| "one"; "two"; "three" |]) {
        yield fun item _ -> li () { $"Item {item}" }
    }
}
export function Cell_c5() {
    return <ul>
        <KeyedFor each={["one", "two", "three"]}>
            {(item, _arg) => <li>
                {concat("Item ", item)}
            </li>}
        </KeyedFor>
    </ul>;
}

To write a component that takes a function child, see tag interfaces.

Extension methods

Spreads, custom and data-* attributes, style objects and refs are chained after the constructor: div(class' = "card").data("id", "42"). See extension methods.

Edit this page