Fable.Lit.Dsl 1.0.1

dotnet add package Fable.Lit.Dsl --version 1.0.1
                    
NuGet\Install-Package Fable.Lit.Dsl -Version 1.0.1
                    
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="Fable.Lit.Dsl" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Fable.Lit.Dsl" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Fable.Lit.Dsl" />
                    
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 Fable.Lit.Dsl --version 1.0.1
                    
#r "nuget: Fable.Lit.Dsl, 1.0.1"
                    
#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.
#:package Fable.Lit.Dsl@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Fable.Lit.Dsl&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Fable.Lit.Dsl&version=1.0.1
                    
Install as a Cake Tool

Fable.Lit.Dsl

A collection of expressive, type-safe DSLs for building UI with Fable.Lit. This repo currently includes:

  • Fable.Lit.Dsl - a clean, idiomatic way to write Lit templates in F#
  • Fable.Lit.Dsl.Shoelace - typed components, attributes, and events for the Shoelace Web Component library

More DSLs may be added over time.


Installation

Add the packages to your project:

dotnet add package Fable.Lit.Dsl
dotnet add package Fable.Lit.Dsl.Shoelace

If using Shoelace, register its assets at app startup:

open Fable.Core.JsInterop
open Fable.Lit.Dsl.Shoelace

// Set the base path for Shoelace assets (icons, etc.)
Shoelace.setBasePath()

// Import the components you need
Shoelace.startImports [|
    importDynamic Shoelace.Asset.Button
    importDynamic Shoelace.Asset.Input
    importDynamic Shoelace.Asset.Dialog
    // ... add more as needed
|]

HTML DSL

The HTML DSL provides a natural, structured way to write Lit templates in F# without stringly-typed markup.

Example

open Fable.Lit.Dsl

view {
    h1 { "Hello, world!" }

    p { "This is the Fable.Lit HTML DSL." }

    button {
        disabled false
        onClick (fun _ -> dispatch Increment)
        "Click me"
    }
}

Builders

  • view { } - Use at the top level of components. Returns a TemplateResult for rendering.
  • template { } - Use for nested fragments inside elements. Returns a Node.
  • el "tag-name" { } - Create custom elements with any tag name.

Custom Elements with el

Use el for one-off custom elements or web components:

view {
    el "my-custom-element" {
        attr "theme" "dark"
        prop "config" {| rows = 10; cols = 5 |}
        boolAttr "enabled" true
        on "custom-event" (fun e -> dispatch (CustomEvent e))
        "Child content"
    }
}

Available attribute helpers:

  • attr "name" value - String attribute
  • boolAttr "name" true - Boolean attribute (present when true, absent when false)
  • prop "name" value - JavaScript property (for complex values or web component properties)
  • on "event-name" handler - Event handler

Fragments with template

Use template when you need to return multiple sibling elements without a wrapper element. This is similar to React fragments (<>...</>).

/// Returns multiple elements without a wrapper div
let userInfo (user: User) =
    template {
        dt { "Name" }
        dd { user.Name }
        dt { "Email" }
        dd { user.Email }
    }

let mainView model =
    view {
        h1 { "User Details" }
        dl {
            // Inserts dt/dd pairs directly into the dl, no wrapper element
            userInfo model.User
        }
    }

In most cases, wrapping content in a div is fine. Use template only when an extra wrapper element would break your HTML structure or CSS styling (like inside <dl>, <table>, <ul>, or flex/grid containers where extra elements affect layout).

Highlights

  • Strongly-typed attributes and events
  • Natural F# computation expression syntax
  • No raw HTML strings
  • Works seamlessly with Lit components and custom elements

Shoelace DSL

Typed, ergonomic bindings for the Shoelace Web Component library.

Example

open Fable.Lit.Dsl
open Fable.Lit.Dsl.Shoelace

view {
    slButton {
        variantPrimary
        onClick (fun _ -> dispatch Save)
        "Save"
    }

    slInput {
        label' "Email"
        placeholder' "you@example.com"
        onSlInput (fun e -> dispatch (EmailChanged e))
    }

    slDialog {
        label' "Confirm"
        open' model.ShowDialog
        onSlRequestClose (fun _ -> dispatch CloseDialog)
        p { "Are you sure?" }
    }
}

Highlights

  • All Shoelace components supported (slButton, slInput, slDialog, slDrawer, etc.)
  • Typed properties (variant, size, disabled', open', label', etc.)
  • Typed events (onSlChange, onSlInput, onSlShow, onSlHide, etc.)
  • Works alongside the HTML DSL

Common Shoelace Properties

// Variants
variantPrimary      // or: variant "primary"
variantSuccess
variantDanger
variantWarning
variantNeutral

// Sizes
sizeSmall           // or: size "small"
sizeMedium
sizeLarge

// States
disabled' true
loading true
open' true
checked' true
clearable true

// Values
value' "text"
label' "Label"
placeholder' "Placeholder"
helpText "Help text"

Common Shoelace Events

onSlChange handler      // Value changed (after interaction)
onSlInput handler       // Real-time input
onSlShow handler        // Element starting to show
onSlAfterShow handler   // Element shown, animations complete
onSlHide handler        // Element starting to hide
onSlAfterHide handler   // Element hidden, animations complete
onSlRequestClose handler // Close requested (dialogs/drawers)
onSlSelect handler      // Menu item selected

Extensibility: Build Your Own DSL

The DSL system is intentionally modular. You can create your own DSL for any Web Component library.

Minimal Example

module MyComponents

open Fable.Lit.Dsl

// Define elements for your web components
let fancyCard = ElementBuilder("fancy-card")
let fancyButton = ElementBuilder("fancy-button")

// Define typed properties
let cardTitle (text: string) = prop "cardTitle" text
let elevation (level: int) = prop "elevation" level

// Define typed events
let onFancyClick (handler: obj -> unit) : Attr = Event("fancy-click", handler)

Usage:

open MyComponents

view {
    fancyCard {
        cardTitle "Welcome"
        elevation 2

        fancyButton {
            onFancyClick (fun _ -> dispatch Click)
            "Click me"
        }
    }
}

You can extend:

  • Components (using ElementBuilder)
  • Attributes (using attr)
  • Properties (using prop)
  • Events (using Event)
  • Boolean attributes (using boolAttr)

This repo models the pattern used by the HTML and Shoelace DSLs.


More Examples

Conditional Rendering

view {
    h2 { "Dashboard" }

    if model.IsLoading then
        slSpinner { }
    else
        div {
            p { $"Welcome, {model.Username}!" }
        }
}

Lists

view {
    ul {
        for item in model.Items do
            li { item.Name }
    }
}

Mixing HTML and Shoelace

view {
    header {
        class' "app-header"
        h1 { "My App" }
    }

    main {
        slCard {
            div {
                slot' "header"
                h3 { "Stats" }
            }
            p { $"Count: {model.Count}" }

            div {
                slot' "footer"
                slButton {
                    variantPrimary
                    onClick (fun _ -> dispatch Increment)
                    "Increment"
                }
            }
        }
    }
}

Using Refs

let inputRef = ref Unchecked.defaultof<Browser.Types.HTMLInputElement>

view {
    slInput {
        bindRef (fun el -> inputRef.Value <- el :?> _)
        label' "Focus me"
    }

    slButton {
        onClick (fun _ -> inputRef.Value.focus())
        "Focus Input"
    }
}

Embedding Raw Lit Templates

open Lit

view {
    h1 { "Mixed Content" }

    // Embed an existing Lit template
    lit (html $"<p>Raw Lit template</p>")

    // Or raw HTML (use sparingly)
    rawHtml "<p>Raw HTML string</p>"
}

Roadmap

The DSL system is intentionally modular, and this repo may grow over time as more UI libraries adopt Web Components.

Potential Future DSLs

  • FluentUI DSL - Microsoft's Fluent Web Components
  • FAST DSL - FAST's design tokens and component model
  • Material Web DSL - Google's Material Web Components
  • Community DSLs - Anyone can build a DSL package for their preferred component library

Future Improvements

  • Additional typed attributes and events
  • Expanded examples and documentation
  • Potential integration with design-token systems
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Fable.Lit.Dsl:

Package Downloads
Fable.Lit.Dsl.Shoelace

Shoelace web component bindings for Fable.Lit.Dsl. Provides strongly-typed F# DSL elements for building Shoelace-based UIs with Fable.Lit.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 46 1/26/2026
1.0.0 47 1/26/2026