Skip to main content

[<ReactComponent>]

Feliz offers a simple way to define React components using the [<ReactComponent>] attribute. This attribute can be applied to a function that returns a React element.

With the help of [<ReactComponent>] Feliz can ensure that the components are correctly transformend and called after transpilation to JavaScript.

info

If you are interested in what happens under the hood, here is a short summary of notable points:

Click to expand
  • Transform all arguments into an JavaScript object.

    Tupled/curried arguments are transformed into object and exactly one argument as (anonymous) record type will be used as object.

    [<ReactComponent>]
    let Component(text: string, count: int) = ...

  • Component will be called using the createElement function from React.

  • Import React automatically.

export default

You can pass a boolean parameter to the attribute to export the component as the default export of the module.

[<ReactComponent(exportDefault = true)>]
let Component() = ...
// or

[<ReactComponent(true)>]
let Component() = ...

import .. from

You can pass string arguments to the attribute to import specific named imports from a module.

Counter: 42

Counter: 10

Show code
NativeCounter.tsx
import React from "react";

interface Props {
init?: number;
}

export function Counter({init = 42}: Props) {
const [count, setCount] = React.useState(init);
return (
<div>
<h1>Counter: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}

[<ReactMemoComponent>]

React memo components can be created using the [<ReactMemoComponent>] attribute. It works the same way as [<ReactComponent>], but wraps the component in React.memo and ensures that it is defined as a const in the generated JavaScript code.

[<ReactMemoComponent>]
let Component(text: string, count: int) =
Html.div [
for i in 1 .. count do
Html.p [
prop.key i
prop.text (sprintf "%d: %s" i text)
]
]

[<ReactLazyComponent>]

React lazy components can be created using the [<ReactLazyComponent>] attribute. It works similarly to [<ReactComponent>], but is intended for components that are loaded dynamically using React.lazy.

There are two ways to define a lazy component with [<ReactLazyComponent>]:

info

Remember that only default exports can be lazy loaded!

[<ReactComponent(true)>] // like this!

From existing component

This approach allows to wrap an existing component in a lazy-loaded component.

If you have optional parameters, you will need to add the arguments you want to use to the wrapper. Otherwise the F# compiler will assume a None for all. (See examples below!)

module Examples.Feliz.ReactLazyComponent

open Feliz

[<ReactLazyComponent>]
let private LazyLists(list: int list option) = Examples.Feliz.RenderingLists.RenderingLists.Example(?list = list)

[<ReactLazyComponent>]
let private LazyListsNoArg = Examples.Feliz.RenderingLists.RenderingLists.Example

[<Fable.Core.Erase; Fable.Core.Mangle(false)>]
type Examples =

[<ReactLazyComponent>]
static member LazyList(?list: int list) = Examples.Feliz.RenderingLists.RenderingLists.Example(?list = list)

[<ReactComponent(true)>]
static member Main() =
Html.div [
Html.h1 "ReactLazyComponent Example"
Html.h2 "With argument"
LazyLists(Some [1;2;3;4;5])
Html.h2 "Without argument"
LazyListsNoArg()
Html.h2 "Using static member"
Examples.LazyList([10;20;30])
]

From path

Alternatively you can specify a path to a module that contains a default export of a React component.

warning

This approach will not have type safety for the component props! It is similiar to writing a JavaScript binding!

module Examples.Feliz.ReactLazyComponentPath

open Feliz

[<ReactLazyComponent>]
let private LazyLists(list: int list option) = React.DynamicImported "./RenderingLists"

[<ReactLazyComponent>]
let private LazyListsNoArg() = React.DynamicImported "./RenderingLists"

[<Fable.Core.Erase; Fable.Core.Mangle(false)>]
type Examples =

[<ReactLazyComponent>]
static member LazyList(?list: int list) = React.DynamicImported "./RenderingLists"

[<ReactComponent(true)>]
static member Main() =
Html.div [
Html.h1 "ReactLazyComponent Example"
Html.h2 "With argument"
LazyLists(Some [1;2;3;4;5])
Html.h2 "Without argument"
LazyListsNoArg()
Html.h2 "Using static member"
Examples.LazyList([10;20;30])
]