A modern, ergonomic starter template for building full‑stack F# applications with Fable.Lit, and Web Components — powered by a brand‑new, strongly‑typed UI DSL that removes the biggest pain points of traditional Lit development.
This template is designed to give you a smooth, productive experience from day one, whether you're building a small prototype or a full production app.
Now updated to .NET 10, Fable 5.0.0‑alpha.22, and FSharp.Core 10.
This is all it takes to build a reactive component with the new Fable.Lit.Dsl:
[<HookComponent>]
let Counter() =
let count, setCount = Hook.useState(0)
view {
slButton {
onClick (fun _ -> setCount(count + 1))
$"Clicked {count} times"
}
}No JSX.
No HTML strings.
No dependency arrays.
Just clean, strongly‑typed F#.
Prefer raw Lit templates instead?
You can still use interpolated string templates directly with html { ... }.
[<HookComponent>]
let Counter() =
let count, setCount = Hook.useState(0)
html $"""
<sl-button @click=${Ev(fun _ -> setCount(count + 1))}>
Count: {state.Count}
</sl-button>
"""For best results, install the VS Code extension for F# template highlighting.
Install the template globally:
dotnet new -i fable-lit-fullstack-templateCreate a new project:
dotnet new fable-lit-fullstack -n MyAppRun it:
cd MyApp
npm install
npm run devYou now have a full F# + Fable + Lit application running with the new DSL baked in.
Lit is a modern, lightweight alternative to React — built directly on native Web Components, not a custom runtime.
If you’re coming from React, you’ll find Lit refreshingly simple, fast, and future‑proof.
React re-renders entire component trees and relies on a virtual DOM diffing algorithm to figure out what changed.
Lit updates only the exact DOM nodes that need to change, using real browser APIs.
It’s faster because it’s simpler.
React + ReactDOM is ~120kb minified.
Lit is ~6kb.
Smaller bundles mean:
- faster startup
- better Lighthouse scores
- better mobile performance
Lit components are Web Components, which means they work everywhere:
- React
- Vue
- Svelte
- Angular
- plain HTML
- server‑rendered apps
- microfrontends
React components only work in React.
Lit uses:
- real JavaScript classes
- real DOM APIs
- real browser standards
No JSX transform.
No custom compiler.
No runtime wrappers.
Web Components are part of the platform.
They don’t get rewritten every 18 months.
Lit’s declarative, composable model maps beautifully to:
- computation expressions
- immutable data
- functional UI patterns
- strongly‑typed DSLs
React’s JSX does not.
React forces you to think about:
useMemouseCallback- dependency arrays
- stale closures
- preventing unnecessary re-renders
Lit updates only the DOM nodes that change.
F# encourages immutable data by default.
The result:
- no memoization
- no dependency arrays
- no re-render storms
- no performance footguns
Just clean, predictable updates.
If you enjoy React’s component model, you’ll feel right at home here.
This template supports:
Use useState, useEffect, and other familiar patterns — without dependency arrays or stale closures.
Switch to Elmish for larger components or full applications.
You get predictable state, pure updates, and no hook rules.
Write clean, strongly‑typed F# instead of JSX or HTML strings.
No editor extensions required.
No virtual DOM.
No diffing.
No reconciliation.
Just direct, surgical DOM updates.
This is what a real page looks like using the DSL.
No HTML strings.
No JSX.
Just clean, composable F#.
module WebLit.ViewCatFactPage
open Lit
open LitRouter
open Fable.Lit.Dsl
open Fable.Lit.Dsl.Shoelace
let private hmr = HMR.createToken()
[<HookComponent>]
let Page (fact: string) =
Hook.useHmr(hmr)
view {
slBreadcrumb {
style "margin: 10px;"
slBreadcrumbItem {
onClick (fun _ -> Router.navigatePath("/"))
"Home"
}
slBreadcrumbItem {
onClick (fun _ -> Router.navigatePath("/cat-facts"))
"Cat Facts"
}
slBreadcrumbItem {
style "font-weight: bold;"
"Fact"
}
}
slCard {
class' "card-overview"
img {
slot' "image"
attr "src" "https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
attr "alt" "A kitten sits patiently between a terracotta pot and decorative grasses."
}
strong { "Fact" }
br { }
div { fact }
small { "Meow!" }
div {
slot' "footer"
slButton {
variantPrimary
pill true
onClick (fun _ -> Router.navigatePath("/cat-facts"))
"Tell me more!!"
}
}
}
}This example demonstrates:
- the
viewbuilder - the Shoelace DSL
- routing
- events
- slots
- attributes
- styling
- nested composition
It shows how the DSL scales to real‑world UI.
Lit is already fast and modern — but writing HTML templates inside F# strings can be awkward and editor‑dependent.
This template solves that with a new DSL that gives you:
This template intentionally mixes two approaches:
Most pages use the new DSL for clarity and ergonomics.
This page demonstrates:
- a plain
htmlstring interpolation template - usage of Shoelace components
- usage of custom-crafted Lit web component controls (
vert-stackandhoriz-stack)
Shoelace components are registered using a simple, reliable system based on [<Literal>] asset paths:
importDynamic Shoelace.Asset.Button
importDynamic Shoelace.Asset.Dialog
importDynamic Shoelace.Asset.DarkThemeNo abstraction layers.
No magic.
Just clean, explicit imports.
npm install
npm run dev
npm run buildIncludes:
- full Fable + .NET debugging
- hot module reloading
- server + client projects
- shared F# code
Future NuGet packages:
- Fable.Lit.Dsl.FluentUI / FAST
This template gives you a modern, friction‑free experience building Web Components and reactive UI in F#.
The new DSL removes years of friction and opens the door to a more expressive, maintainable style of UI development.
Have fun — and build something amazing.