Oxpecker.Solid 0.7.0

dotnet add package Oxpecker.Solid --version 0.7.0
                    
NuGet\Install-Package Oxpecker.Solid -Version 0.7.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Oxpecker.Solid" Version="0.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Oxpecker.Solid" Version="0.7.0" />
                    
Directory.Packages.props
<PackageReference Include="Oxpecker.Solid" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Oxpecker.Solid --version 0.7.0
                    
#r "nuget: Oxpecker.Solid, 0.7.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=Oxpecker.Solid&version=0.7.0
                    
Install Oxpecker.Solid as a Cake Addin
#tool nuget:?package=Oxpecker.Solid&version=0.7.0
                    
Install Oxpecker.Solid as a Cake Tool


Oxpecker.Solid

Fable (4.23.0+) opinionated bindings for the Solid.js, Solid-router.js and Solid-meta.js libraries based on Oxpecker's view engine. This library DOES NOT depend on Fable.Solid package.

Medium article: Oxpecker goes full stack

Nuget package dotnet add package Oxpecker.Solid

Markup example:

open Oxpecker.Solid // this library namespace
open Browser // Fable browser bindings (needed for console.log)

[<SolidComponent>] // this attribute is required to compile components to JSX that Solid understands
let itemList items = // regular function arguments, no props!
    let x, setX = createSignal 0 // just sample of reactive state usage
    ul() { // ul tag
        For(each = items) { // Solid's built-in For component
            yield fun item index -> // function called for each item
                li() { // li tag
                    span() { index() } // index is reactive getter from Solid
                    span() { item.Name } // string field inside span tag
                    button(onClick=fun _ -> console.log("Removing item...")) { // onClick event handler
                        "Remove" // button text
                    }
                }
        }
    }

Documentation:

You mostly need to refer to the official site for the Solid.js documentation: https://docs.solidjs.com.

You can also check TODO list sample in the repository to get a better understanding of how the code will look like in your application.

There are some details that are specific to Oxpecker's view engine implementation:

SolidComponent attribute

This attribute tells Fable compiler plugin (Oxpecker.Solid.FablePlugin) that this function should be transformed into Solid compatible JSX during build time. It has no effect at runtime. This plugin is referenced by Oxpecker.Solid package and will be automatically added to your project when you install it, no manual reference is needed.

Props

With this library you can forget about props and special helpers created for managing them. You can use regular function arguments instead. You can be as creative as you want with your function arguments, they can be records, functions, ref cells (especially useful for passing refs from child to parent) or any other regular F# types. If you do want to use props and have merge and split functionality for integrating third-party library, you should use Partas.Solid library, that heavily relies on them.

Fragment element

Fragment element get transformed in Solid's <>...</>tag. It's used to wrap multiple elements without creating a new DOM element.

For and Index elements

These elements are used to iterate over a list of items (you can read about the difference here). You'll need to use yield keyword before the iterating function because currently F# computation expressions don't allow for implicit yield for functions.

Custom HTML tags / Web Components

Custom elements can be useful for integration with Web Component based libraries. You can create one by inheriting from RegularNode (or VoidNode):

namespace Oxpecker.Solid.MyTags

[<CompiledName("my-tag")>]
type MyTag () =
    inherit RegularNode()
    member this.myAttr
        with set (value: string) = ()

Make sure you put your custom element type definition in a separate module (not to the same module with its usage) in a namespace starting from Oxpecker.Solid for compiler plugin to transform it correctly.

Components

There are two ways you can create components in Oxpecker.Solid:

1. Regular functions

This is the most common way to create components and should be used by default.

// without children
[<SolidComponent>]
let Component1 (getText: Accessor<string>) =
    h1(onClick = fun _ -> console.log(getText())) {
        getText()
    }

// with children
[<SolidComponent>]
let Component2 (hello: string) (children: #HtmlElement) =
    h1() {
        hello
        children
    }

// usage
[<SolidComponent>]
let Test () =
    let getText, _ = createSignal "Hello,"

    div() {
        Component1 getText
        Component2 "world" <| span() {
            "!"
        }
    }
2. Types

This is useful when you are creating a component with a lot of properties, and you want to make some of the optional, so component user won't have to initialize all of them.

// defniniion
type MyButton() =
    // properties with defaults
    member val btnColor = "" with get, set
    member val onClick: MouseEvent -> unit = ignore with get, set
    member val ariaLabel = "" with get, set

    [<SolidComponent>]
    member this.WithChildren(content: #HtmlElement) = // Can be any name
        button(type'="button", class'= $"text-base ml-2.5 hover:text-blue-500 {this.btnColor}",
               onClick = this.onClick, ariaLabel = this.ariaLabel) {
            content
        }

// usage
[<SolidComponent>]
let Test() =
    div() {
        MyButton(onClick = (fun _ -> alert "Deleted"), btnColor = "text-red-500") // ariaLabel is optional
            .WithChildren(span() { "🗑️" })
    }

If you want to build even more complicated components that are compiled into JSX components (rather than JS function calls), heavily depends on props spread, merge and split, you can use Partas.Solid library, which supports all of that.

Context

This library doesn't provide support for React-like context. I strongly believe it's an antipattern, and encourage you to use global signals or stores instead. If you do want to use Context, have a look at Partas.Solid library, where it is supported.

Special JSX attributes

Note that attr:, on:, bool:, ref attributes are exposed as F# methods in the API: elem.on(...), elem.attr(...) etc. Also, style and class' are attributes when accepting string, while style' and classList are methods when accepting object (to be used with createObj).

Note: when using ref(nativeElem) make sure that nativeElem is mutable (e.g. let mutable nativeElem = Unchecked.defaultof<HTMLDivElement>).

Store

Similar to the original implementation in Fable.Solid , store has a special helper for updating its fields:

setStore // store setter
    .Path // special property to access store fields
    .Map(_.data) // choose which field to update
    .Update(newData) // update field with a new value

Resource

Again, just as in the original implementation in Fable.Solid, resource is a special object, so instead of JS resource() call, you'll need to use resource.current in F#.

Router

Router namespace is Oxpecker.Solid.Router. It contains router related components and functions. To render a router you still need to decorate router function with SolidComponent attribute:

open Oxpecker.Solid.Router

[<SolidComponent>]
let MyRouter () =
    Router() {
        Route(path="/", component'=Home)
        Route(path="/about", component'=About)
    }

render (MyRouter, document.getElementById "root")

You still need to add a separate reference to @solidjs/router in your package.json.

Meta

Meta namespace is Oxpecker.Solid.Meta. It contains elements to be rendered in the application's <head> section:

open Oxpecker.Solid.Meta

[<SolidComponent>]
let Root () =
    MetaProivder() {
        Title() { "My App" }
    }

You still need to add a separate reference to @solidjs/meta in your package.json.

Aria

Similar to the original Oxpecker.ViewEngine additional Aria attributes reside in a separate module, so you need to write open Oxpecker.Solid.Aria to access them.

Lazy

For components lazy loading you'll need to use lazy' function together with another importComponent function.

lazy'(fun () -> importComponent("./ComponentA"))

will be translated to

lazy(() => import("./ComponentA"))
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.7.0 126 4/12/2025
0.6.0 286 3/18/2025
0.5.3 67 3/15/2025
0.5.2 175 3/1/2025
0.5.0 175 2/24/2025
0.4.3 240 1/29/2025
0.4.2 327 1/3/2025
0.4.1 111 12/20/2024
0.4.0 126 12/5/2024
0.3.0 124 11/13/2024
0.2.2 104 10/31/2024
0.2.1 103 10/29/2024
0.2.0 118 10/22/2024
0.1.0 99 10/17/2024
0.0.2 123 10/10/2024
0.0.1 110 10/10/2024

Secondary primitives added