Full-stack, functional reactive web programming for .NET

Develop microservices, client-server web applications, reactive SPAs, and more in C# or F#.

Get started
$ dotnet new install WebSharper.Templates
DSL + AI

DSL & AI Integration

Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.

Learn more
[<JavaScript>]
module Client =
    type IndexTemplate = Template<"wwwroot/index.html", ClientLoad.FromDocument>

    let People =
        ListModel.FromSeq [
            "John"
            "Paul"
        ]

    [<SPAEntryPoint>]
    let Main () =
        let newName = Var.Create ""
        IndexTemplate.Main()
            .ListContainer(
                People.View.DocSeqCached(fun (name: string) -> 
                    IndexTemplate.ListItem().Name(name).Doc()
                )
            )
            .Name(newName)
            .Add(fun _ ->
                People.Add(newName.Value)
                newName.Value <- ""
            )
            .Doc()
        |> Doc.RunById "main"
Terminal
VITE v6.3.5 ready in 663 ms
Network: use --host to expose
press h + enter to show help

Trusted by fast-growing companies around the world

Functional, reactive applications with WebSharper.UI

Bind composite data models to your reactive user interfaces. The flow of reactive values through the code yet concise, readability.

let x = Var.Create 1
let y = Var.Create 1

let addNumbers =
    div [] [
        p [] [text "x "; Doc.InputType.IntUnchecked [] x]
        p [] [text "y "; Doc.InputType.IntUnchecked [] y]
        p [] [
            textView (View.Map2 (fun x y -> $"x + y = {x + y}") x.View y.View)
        ]
    ]

addNumbers |> Doc.RunById "main"
Search

Model-View-Update architecture with WebSharper.Mvu

Structure client-side applications with clear separation of concerns between logic and display. Based on WebSharper.UI, Mvu brings Elm-like architecture to WebSharper.

Learn more
type Model = { name: string }
let InitModel = { name = "" }

type Message =
    | SetName of string

let Update message model =
    match message with
    | SetName n -> { model with name = n }

let View dispatch (view: View) =
    div [] [
        input [
            attr.value view.V.name
            on.change (fun el _ ->
                dispatch (SetName el?value))
        ] []
    ]

let Main = App.CreateSimple InitModel Update View |> App.Run

Build React components

You can also use widespread libraries such as React to build applications. The syntax is as convenient as JSX, with the added benefit of strong typing.

Learn more
type Person = { name: string; age: int }

type ShowPerson(props) =
    inherit React.Component(props)

    override this.Render() =
        ul [attr.className "person"] [
            li [] [text ("Name: " + this.Props.name)]
            li [] [text ("Age: " + string this.Props.age)]
        ]                                                                
                                                            
                                                        

Web APIs and server-side JSON and HTML

Declare your URLs as a C# or F# endpoint type, and WebSharper handles the dispatch. Create safe internal links from endpoint values.

You can share the same rendering code and templates between the server and the client.

Automatically parse JSON requests and generate JSON responses based on your types.

Learn more
// Define the application's endpoints
type EndPoint =
    | [<EndPoint "GET /articles">]
        ArticleList
    | [<EndPoint "GET /article">]
        Article of id: int

// Define the content served by these endpoints
let Website =
    Application.MultiPage (fun (ctx: Context<_>) endpoint ->
        match endpoint with
        | ArticleList ->
            Content.Page(
                div [] [
                    h1 [] [text "Welcome!"]
                    a [attr.href (ctx.Link (Article 42))] [
                        text "Go to article 42"
                    ]
                ]
            )
        | Article id ->
            Content.Json { id = id
                            title = "Hello!"
                            date = System.DateTime.Now }
    )

Client-side routing

Write SPAs with the same routing API on the client side as you would use on the server side.

Learn more
let router = Router.Infer<EndPoint>()
let route = router.Install(Article 1)

let view =
    route.View.Map (function
        | Article id ->
            div [] [
                h1 [] [text $"This is article {id}"]
                a [attr.href (router.Link Articles)] [text "Go back to the list of articles"]
            ]
        | Articles ->
            h1 [] [text "List of articles"]
    )

let doc = Doc.BindView id view
Doc.RunById "main" doc

Use powerful language constructs on the client side

F#: Query expressions, async workflows, sequence expressions...

C#: LINQ queries, asynchronous Tasks, generators...

Learn more
type Article = { id: int; title: string; date: DateTime }

let start = DateTime(2025, 9, 1)

let articles : Article list = [
    { id = 1; title = "Welcome to WebSharper"; date = DateTime(2025, 9, 20) }
    { id = 2; title = "Async workflows 101"; date = DateTime(2025, 8, 15) }
    { id = 3; title = "Query expressions on client"; date = DateTime(2025, 9, 28) }
    { id = 4; title = "Remoting tips"; date = DateTime(2025, 7, 5)  }
]
// Query expressions (LINQ for F#)
let recentIds =
    query {
        for article in articles do
        where (article.date > start)
        sortByDescending article.date
        select article.id
    }
// Async workflows (run non-blocking work on the client)
let loadAndShow =
    async {
        do! Async.Sleep 10
        Console.Log(sprintf "Recent IDs: %A" (recentIds |> Seq.toList))
    }    

Async.Start loadAndShow

// Sequence expressions (lazy generators)
let squares =
    seq {
        for i in 1 .. 5 do
            yield i * i
    }   
    
Console.Log(String.concat ", " (squares |> Seq.map string))

Type-safe HTML templating

Use HTML templates to keep your logic and UI clearly separate and facilitate the collaboration between programmers and designers.

Learn more
IndexTemplate.Main()
    .Name(newName)
    .Add(fun _ ->
        People.Add(newName.Value)
        newName.Value <- ""
    )
    .Remove(fun _ -> 
        People.Remove newName.Value                                                            
    ) 
    .Doc()
                                                            
                                                        

Remoting with ease

Seamlessly call server-side asynchronous functions from the client side.

AJAX call, JSON serialization... Everything is handled automatically.

Learn more
// Server-side, remote-callable function
[<Remote>]
let ServerFn () : Async<string> =
    async { return "Hello, world!" }

// Client-side function
[<JavaScript>]
let CallServer () : unit =
    async {
        let! result = ServerFn()
        Console.Log(result)
    }
    |> Async.Start
                                                            
                                                        

Powerful UI abstractions

Declare full interactive forms in just a few lines of code with WebSharper Forms.

let LoginForm () =
    Form.Return (fun user pass -> user, pass)
    <*> (Form.Yield ""
        |> Validation.IsNotEmpty "Must enter a username")
    <*> (Form.Yield ""
        |> Validation.IsNotEmpty "Must enter a password")
    |> Form.WithSubmit
    |> Form.Run (fun (u, p) ->
        JS.Alert("Welcome, " + u + "!")
    )
    |> Form.Render (fun user pass submit ->
        form [] [
            div [] [label [] [text "Username: "]; Doc.InputType.Text [] user]
            div [] [label [] [text "Password: "]; Doc.InputType.Password [] pass]
            Doc.Button "Log in" [] submit.Trigger
            div [] [
                Doc.ShowErrors submit.View (fun errors ->
                    errors
                    |> Seq.map (fun m -> p [] [text m.Text])
                    |> Doc.Concat)
            ]
        ]
    )
    
LoginForm() |> Doc.RunById "main"
Search

Web Workers without hassle

Write client-side parallel code directly in your code.

The compiler automatically creates the separate trimmed JavaScript file for the web worker.

Worker.fs
let worker = new Worker(fun self ->
  // This code runs in the worker:
  self.OnMessage <- fun e ->
      Console.Log(
        "Received message from main thread: " 
        + string e.Data
      )
)

// This code runs in the main thread:
worker.PostMessage("Hello, worker!")
index.js
var worker = new Worker("worker.js");
worker.postMessage("Hello, worker!");
                                            
                                        
worker.js
self.onmessage = function (e) {
    console.log(
       "Received message from main thread: "
           + e.data.toString());
}
                                            
                                        
Terminal

Get Started

Instant access to the power of WebSharper

Get started
$ dotnet new install WebSharper.Templates