Skip to main content

Output JSX

Change file extension

If you want to use JSX output you need to change the file extension of your transpiled files to *.jsx. This is required for the JSX output to be correctly interpreted by bundlers like webpack or tools like eslint. You can do this by updating your Fable transpile command:

dotnet fable Example.fsproj -e .jsx

This does not impact your ability to use the established Feliz API, which will still work as expected in *.jsx files.

Currently, the default way for Feliz to create React Elements is through React.createElement. This allows us to fully leverage f# list expression syntax and makes it easy to create elements dynamically. However, using modern JSX output makes the transpiled code more readable and easier to debug, and might lead to performance increases.

Check out the official React docs!

For more detailed information about React concepts and APIs, please refer to the official documentation for createElement.

Thanks to the awesome work of the Fable team (big shoutout to @MangelMaxime), the support for JSX output was greatly increased. And there are two ways to use it in Feliz. One is the Feliz.JSX module, which provides a similar API to the existing Feliz module, but outputs JSX. The other is through template strings, which allow you to write JSX directly in your F# code.

In my opinion this feature is still experimental, and it might be best to mix and match between the existing Feliz API and the new JSX output, depending on your needs. But it's definitely worth exploring!

Using the JSX module

The Feliz.JSX module uses Fables JSX.create function to output JSX. This abstraction from JSX.create to JSX output is done directly on the Fable compiler level, resulting in a very clean output.

If you want to give it a try you can open Feliz.JSX in your file. This will shadow the existing Html.* so you can easily switch between the two.

Below you can find an example for using the Feliz.JSX module. It fully duplicates the syntax from the default Html.* type. Make sure to check out the transpiled outputs!

Hello from JSX!

This is a paragraph inside a div.

Counter - Reactivity Test

  • No items
Show code
JSX.fs
module Examples.Guides.JSXModule

open Feliz
open Feliz.JSX

[<ReactComponent(true)>]
let TestComponent() =
let counter, setCounter = React.useState(0)

Html.div [
prop.id "my-div"
prop.className "container"
prop.children [
Html.h1 "Hello from JSX!"
Html.p "This is a paragraph inside a div."
Html.div [
Html.h1 "Counter - Reactivity Test"
Html.button [
prop.text "Increment"
prop.onClick (fun _ -> setCounter(counter + 1))
]
Html.ul [
if counter = 0 then
Html.li "No items"
else
for i in 1..counter do
Html.li [
prop.key i
prop.text (sprintf "Item %d" i)
]
]
]
]
]

Alias the Feliz.JSX module

If you want to mix both JSX output and previous output in one file you can alias the module:

type Jsx = Feliz.JSX.Html

Jsx.div [
Jsx.h1 "Hello from JSX!"
Jsx.p "This is a paragraph inside a div."
]

Hello from JSX!

This is a paragraph inside a div.

Counter - Reactivity Test

  • No items
Show code
JSXAlias.fs
module Examples.Guides.JSXModuleAlias

open Feliz

type JSX = Feliz.JSX.Html

[<ReactComponent(true)>]
let TestComponent() =
let counter, setCounter = React.useState(0)

JSX.div [
prop.id "my-div"
prop.className "container"
prop.children [
JSX.h1 "Hello from JSX!"
JSX.p "This is a paragraph inside a div."
JSX.div [
JSX.h1 "Counter - Reactivity Test"
JSX.button [
prop.text "Increment"
prop.onClick (fun _ -> setCounter(counter + 1))
]
Html.ul [
if counter = 0 then
Html.li "No items"
else
for i in 1..counter do
Html.li [
prop.key i
prop.text (sprintf "Item %d" i)
]
]
]
]
]

FAQ

These FAQs are errors or issues that you might encounter when using the JSX module. (Tested for Fable version 5.0.0-alpha.14).

match ... with in children 🐛

Using match ... with directly inside prop.children is currently not correctly handled by the fable compiler and will lead to runtime errors. A workaround is to use if syntax instead or to bind the result of the match to a variable first.

info

Tracked in this GitHub issue.

[<ReactComponent(true)>]
let TestComponent() =
let counter, setCounter = React.useState(0)
Html.ul [
match counter with
| 0 -> Html.li "No items!"
| items ->
for i in 1..items do
Html.li [
prop.key i
prop.text (sprintf "Item %d" i)
]
]

conditional properties ⚠️

error FABLE: Expecting a static list or array literal (no generator) for JSX props

info

Tracked in this GitHub issue.

You might encounter issues when using conditional properties.

In Feliz you normally can do conditional properties in any way F# list syntax allows.

Html.div [
if isActive then prop.custom("data-active", true)
if isDisabled then prop.className "disabled"
]

This does currently not work in JSX syntax. This issue arises due to JSX output syntax where conditional properties are done like this:

<div>
data-active={isActive ? true : undefined}
className={isDisabled ? "disabled" : undefined}
</div>

Following this line of thought, in which the property key is always present we need to shift the conditional inside the property value. So the correct way to do conditional properties in JSX syntax is:

Html.div [
prop.custom("data-active", isActive)
prop.className(if isDisabled then "disabled" else "")
]

Using template strings

info

This is based on the following blog post by @alfonsogcnunez

Using f# template strings you can write JSX directly in your f# code. The syntax used is very similiar to the one introduced by Lit, which is a popular library for building web components.

We use Html.jsx $""" .. """ to define a JSX template. Inside the template you can use ${ ... } to insert f# expressions.

Hello from JSX Template!

This is a paragraph inside a div.

Counter - Reactivity Test

  • No items
Show code
RawJSXTemplate.fs
module Examples.Guides.JSXTemplate

open Feliz
open Feliz.JSX


[<ReactComponent(true)>]
let TestComponent() =
let counter, setCounter = React.useState(0)

let ChildList =
if counter = 0 then
Html.jsx $"""
<li>No items</li>
"""
else
React.Fragment [
for i in 1..counter do
Html.jsx $"""
<li key={i} id={sprintf "item-%d" i}>Item {i}</li>
"""
]
Html.jsx
$"""
<div>
<h1>Hello from JSX Template!</h1>
<p>This is a paragraph inside a div.</p>
<div>
<h1>Counter - Reactivity Test</h1>
<button onClick={fun _ -> setCounter(counter + 1) } >Increment</button>
<ul>
{ChildList}
</ul>
</div>
</div>
"""

Editor support

To get syntax highlighting and intellisense support in VSCode you can use the plugin Highlight templates in F#.

danger

Make sure to follow the exact syntax for template strings to get hightlighting and intellisense support.

// This must be one line with a linebreak after the $"""
Html.jsx $"""
<div>
<h1>Hello from JSX Template!</h1>
<p>This is a paragraph inside a div.</p>
</div>
"""
// The closing """ must be on a new line