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
}

Attributes are easy to tell apart from children, and each element has a clear start and end, much like XML. To build attributes programmatically, for example from a list, spread an object onto the element with .spread.

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'.

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

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
}

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

Embed raw JSX (Fable's JSX.jsx) through a small helper:

let inline toHtmlElement (func: string -> JSX.Element) (value: string) : HtmlElement =
    unbox (func value)
div () {
    toHtmlElement JSX.jsx "<button>Button</button>"
}

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}" }
    }
}

Extension methods

Extension methods cover what constructor parameters cannot: spreads, custom and data-* attributes, style objects and refs.

div(style = "padding: .5em; border: 1px dashed gray").data("state", "ready").attr("aria-live", "polite") {
    "This div has data-state and aria-live attributes."
}
export function Cell_c5() {
    return <div style="padding: .5em; border: 1px dashed gray"
        data-state="ready"
        aria-live="polite">
        This div has data-state and aria-live attributes.
    </div>;
}

See extension methods for the full list.

Edit this page