|
| 1 | +--- |
| 2 | +title: Getting Started |
| 3 | +sidebar_position: 1 |
| 4 | +--- |
| 5 | + |
| 6 | +import Tabs from '@theme/Tabs'; |
| 7 | +import TabItem from '@theme/TabItem'; |
| 8 | + |
| 9 | +Feliz is a streamlined wrapper around the React API. You can use it either as standalone library or use the Feliz template to get started quickly. |
| 10 | + |
| 11 | +## Install pre-requisites |
| 12 | + |
| 13 | +You'll need to install the following pre-requisites to use Feliz. |
| 14 | + |
| 15 | +1. [.NET SDK](https://dotnet.microsoft.com/en-us/download) - You need to have the .NET SDK installed. |
| 16 | +2. [Node.js](https://nodejs.org/en/download/) - You need to have Node.js installed. This will also install npm which is the package manager used to install JavaScript packages. |
| 17 | +3. IDE with F# support. My personal recommendation is [Visual Studio Code](https://code.visualstudio.com/) with the [Ionide-fsharp](https://marketplace.visualstudio.com/items?itemName=Ionide.Ionide-fsharp) extension. |
| 18 | + |
| 19 | +Verify installation by running the following commands in your terminal |
| 20 | + |
| 21 | +```bash |
| 22 | +dotnet --version |
| 23 | +node --version |
| 24 | +npm --version |
| 25 | +``` |
| 26 | + |
| 27 | +## Template [](https://www.nuget.org/packages/Feliz.Template) |
| 28 | + |
| 29 | +The Feliz template is a dotnet template that can be installed using the following command |
| 30 | + |
| 31 | +```bash |
| 32 | +dotnet new -i Feliz.Template |
| 33 | +``` |
| 34 | + |
| 35 | +:::info |
| 36 | +Remember to update the template every once in a while especially before you scaffold a new project so that you get the latest updates from the template. |
| 37 | +::: |
| 38 | + |
| 39 | +After installing the latest version of the template, you can create a new project using |
| 40 | + |
| 41 | +```bash |
| 42 | +dotnet new feliz -n AwesomeApp |
| 43 | +``` |
| 44 | + |
| 45 | +Now you are good to go. This will scaffold the application inside the newly created AwesomeApp directory! 🎉 |
| 46 | + |
| 47 | +The Feliz Template has the following features: |
| 48 | + |
| 49 | +- Uses Vite as the build tool |
| 50 | +- Uses Vitest as testing framework |
| 51 | +- Uses Tailwind CSS for styling with vs code extension for autocomplete in f# files. |
| 52 | +- Uses Fantomas for code formatting with vs code settings to run on save |
| 53 | +- .NET central package management using Directory.Packages.props and Directory.Build.props to make splitting into multiple projects easier in the future. |
| 54 | +- .editorconfig file to maintain consistent coding styles between different editors and IDEs. |
| 55 | + |
| 56 | +## Add Feliz to an existing project [](https://www.nuget.org/packages/Feliz) |
| 57 | + |
| 58 | +To use Feliz you have to install the .NET dependency as well as the correct JavaScript dependencies for React. You can do this manually or automatically using [Femto](../ecosystem/Tools/Femto.mdx). |
| 59 | + |
| 60 | +### Automatically |
| 61 | + |
| 62 | +Requires [Femto](../ecosystem/Tools/Femto.mdx) installed locally or globally. |
| 63 | + |
| 64 | +Run the following command in your project directory |
| 65 | + |
| 66 | +```bash |
| 67 | +# Global |
| 68 | +femto install Feliz |
| 69 | + |
| 70 | +# local |
| 71 | +dotnet femto install Feliz |
| 72 | +``` |
| 73 | + |
| 74 | +### Manually |
| 75 | + |
| 76 | +1. Install .NET Feliz package via nuget. |
| 77 | + |
| 78 | + <Tabs defaultValue='PackageReference'> |
| 79 | + |
| 80 | + <TabItem value="CLI" label=".NET CLI"> |
| 81 | + ``` |
| 82 | + dotnet add package Feliz |
| 83 | + ``` |
| 84 | + </TabItem> |
| 85 | + |
| 86 | + <TabItem value="PackageReference" label="PackageReference"> |
| 87 | + ```bash title="Project file" |
| 88 | + <PackageReference Include="Feliz" Version="2.9.0" /> |
| 89 | + ``` |
| 90 | + </TabItem> |
| 91 | + |
| 92 | + <TabItem value="CPM" label="CPM"> |
| 93 | + ```bash title="Directory.Packages.props" |
| 94 | + <PackageVersion Include="Feliz" Version="2.9.0" /> |
| 95 | + ``` |
| 96 | + |
| 97 | + ```bash title="Project file" |
| 98 | + <PackageReference Include="Feliz" /> |
| 99 | + ``` |
| 100 | + </TabItem> |
| 101 | + |
| 102 | + <TabItem value="PaketCLI" label="Paket CLI"> |
| 103 | + ```bash |
| 104 | + paket add Feliz |
| 105 | + ``` |
| 106 | + </TabItem> |
| 107 | + |
| 108 | + <TabItem value="Script" label="Script & Interactive"> |
| 109 | + ```fsharp |
| 110 | + #r "nuget: Feliz" |
| 111 | + ``` |
| 112 | + </TabItem> |
| 113 | + |
| 114 | + </Tabs> |
| 115 | + |
| 116 | + :::info |
| 117 | + Go to the [nuget](https://www.nuget.org/packages/Feliz/latest) page to find the latest versions and installation methods |
| 118 | + ::: |
| 119 | + |
| 120 | +2. Install JavaScript dependencies via npm or yarn |
| 121 | + |
| 122 | +```bash |
| 123 | +npm install react react-dom |
| 124 | +``` |
| 125 | + |
| 126 | +## From Scratch |
| 127 | +This will be more in the style of a follow along than a raw description. |
| 128 | + |
| 129 | +:::info |
| 130 | +This was done with version `@8.0.2`, if you want to use `@latest` there might be changes to the workflow below |
| 131 | +::: |
| 132 | + |
| 133 | +1. Install via build tool, in this case we will use Vite. Install build tool if required. |
| 134 | + |
| 135 | + ```bash |
| 136 | + npm create vite@8.0.2 . |
| 137 | + ``` |
| 138 | + |
| 139 | + 1. Use Package name of your choice |
| 140 | + 2. Select "React" framework |
| 141 | + 3. Select "JavaScript" variant |
| 142 | + 4. Select "No" for rolldown |
| 143 | + 5. Select "No" for install and start (or "Yes" and just cancel) |
| 144 | + |
| 145 | +2. Delete JavaScript files and dependencies: |
| 146 | + |
| 147 | + - Files in `./src/` folder |
| 148 | + - `eslint.config.js`, as we will write f# |
| 149 | + - `eslint` and `types` dependencies in `package.json` |
| 150 | + |
| 151 | + ```diff |
| 152 | + "devDependencies": { |
| 153 | + - "@eslint/js": "^9.36.0", |
| 154 | + - "@types/react": "^19.1.16", |
| 155 | + - "@types/react-dom": "^19.1.9", |
| 156 | + "@vitejs/plugin-react": "^5.0.4", |
| 157 | + - "eslint": "^9.36.0", |
| 158 | + - "eslint-plugin-react-hooks": "^5.2.0", |
| 159 | + - "eslint-plugin-react-refresh": "^0.4.22", |
| 160 | + "globals": "^16.4.0", |
| 161 | + "vite": "^7.1.7" |
| 162 | + } |
| 163 | + ``` |
| 164 | + |
| 165 | + - "lint" Script |
| 166 | + |
| 167 | + ```diff |
| 168 | + "scripts": { |
| 169 | + "dev": "vite", |
| 170 | + "build": "vite build", |
| 171 | + - "lint": "eslint .", |
| 172 | + "preview": "vite preview" |
| 173 | + }, |
| 174 | + ``` |
| 175 | + |
| 176 | +3. Init .NET tools and install fable |
| 177 | + |
| 178 | +```bash |
| 179 | +dotnet new tool-manifest |
| 180 | +``` |
| 181 | + |
| 182 | +Will create a `./config/dotnet-tools.json` file |
| 183 | + |
| 184 | +```bash |
| 185 | +dotnet tool install fable |
| 186 | +``` |
| 187 | + |
| 188 | +Install Fable locally in `./config/dotnet-tools.json`. |
| 189 | + |
| 190 | +Ensure installation with |
| 191 | + |
| 192 | +```bash |
| 193 | +dotnet fable --version |
| 194 | +``` |
| 195 | + |
| 196 | +4. Init F# project (and solution) |
| 197 | + |
| 198 | +```bash |
| 199 | +dotnet new console -lang F# -o src -n AwesomeApp |
| 200 | +``` |
| 201 | + |
| 202 | +:::info |
| 203 | +You can specify framework with `-f net8.0`. This was done using .NET 8 |
| 204 | +::: |
| 205 | + |
| 206 | +**Optional** Create solution file and add project to it. This is useful if you plan to add more projects in the future. You can build and restore multiple projects at once with a sln file. |
| 207 | + |
| 208 | +```bash |
| 209 | +dotnet new sln -n AwesomeApp |
| 210 | +``` |
| 211 | + |
| 212 | +```bash |
| 213 | +dotnet sln add src/AwesomeApp.fsproj |
| 214 | +``` |
| 215 | + |
| 216 | +5. Add Feliz dependency to F# project `src/AwesomeApp.fsproj` |
| 217 | + |
| 218 | +Check existing versions and installation methods on [nuget](https://www.nuget.org/packages/Feliz/latest) |
| 219 | + |
| 220 | +```bash |
| 221 | +dotnet add ./src/AwesomeApp.fsproj package Feliz |
| 222 | +``` |
| 223 | + |
| 224 | +This will create a new xml `<PackageReference>` entry in the project file. |
| 225 | + |
| 226 | +```xml |
| 227 | +<ItemGroup> |
| 228 | + <PackageReference Include="Feliz" Version="2.9.0" /> |
| 229 | +</ItemGroup> |
| 230 | +``` |
| 231 | + |
| 232 | +:::info |
| 233 | +Instead of running the console command you can also add the `<PackageReference>` entry manually to the project file. |
| 234 | +::: |
| 235 | + |
| 236 | +6. Create Minimal Feliz app |
| 237 | + |
| 238 | +F# requires its source file to be listed in the project file. Open `src/AwesomeApp.fsproj` and add another file to it: |
| 239 | + |
| 240 | +```xml |
| 241 | +<ItemGroup> |
| 242 | + <Compile Include="Components.fs" /> |
| 243 | + <Compile Include="Program.fs" /> |
| 244 | +</ItemGroup> |
| 245 | +``` |
| 246 | + |
| 247 | +:::danger |
| 248 | +The order of the `<Compile>` entries matters. F# compiles top-to-bottom in this list. Ensure that `Components.fs` is listed before `Program.fs` as we will use components defined in `Components.fs` inside `Program.fs`. |
| 249 | +::: |
| 250 | + |
| 251 | +Next create the actual file `src/Components.fs` and update the content of the f# files: |
| 252 | + |
| 253 | +```fsharp title="src/Components.fs" |
| 254 | +namespace App |
| 255 | +
|
| 256 | +open Feliz |
| 257 | +
|
| 258 | +type Components = |
| 259 | +
|
| 260 | + [<ReactComponent>] |
| 261 | + static member Counter() = |
| 262 | + let (count, setCount) = React.useState(0) |
| 263 | + Html.div [ |
| 264 | + Html.h1 $"Count: {count}" |
| 265 | + Html.button [ |
| 266 | + prop.onClick (fun _ -> setCount(count + 1)) |
| 267 | + prop.text "Increment" |
| 268 | + ] |
| 269 | + ] |
| 270 | +``` |
| 271 | + |
| 272 | +```fsharp title="src/Program.fs" |
| 273 | +open Feliz |
| 274 | +open Fable.Core |
| 275 | +
|
| 276 | +/// "root" must match the `id` in ./index.html |
| 277 | +let reactRoot = ReactDOM.createRoot(Browser.Dom.document.getElementById("root")) |
| 278 | +
|
| 279 | +reactRoot.render(App.Components.Counter()) |
| 280 | +``` |
| 281 | + |
| 282 | +7. Update `index.html` to load the fable output. |
| 283 | + |
| 284 | +```html |
| 285 | +<body> |
| 286 | + <div id="root"></div> |
| 287 | + <script type="module" src="/src/fableoutput/Program.fs.jsx"></script> |
| 288 | +</body> |
| 289 | +``` |
| 290 | + |
| 291 | +8. Add transpile scripts to `package.json` |
| 292 | + |
| 293 | +```json |
| 294 | +"scripts": { |
| 295 | + "fable": "dotnet fable src/AwesomeApp.fsproj -o src/fableoutput/ -e fs.jsx", |
| 296 | + "dev": "npm run fable -- --watch --run vite", |
| 297 | + "build": "npm run fable -- --run vite build", |
| 298 | + "preview": "vite preview" |
| 299 | + }, |
| 300 | +``` |
| 301 | + |
| 302 | +- `npm run fable` will transpile the f# code to javascript |
| 303 | + - `src/AwesomeApp.fsproj` the target project |
| 304 | + - `-o src/fableoutput/` the output directory |
| 305 | + - `-e fs.jsx` the file extension to use for the output files |
| 306 | +- `npm run dev` will run the fable command in watch mode and start vite |
| 307 | + |
| 308 | +9. Update .gitignore to ignore fable output |
| 309 | + |
| 310 | +There are some cases in which you might want to keep the generated files in source control, but in most cases you want to ignore them. |
| 311 | + |
| 312 | +Update the `.gitignore` file to include the fable output directory and remove ignore on `*.sln` files to keep F# solution files in source control. |
| 313 | + |
| 314 | +```diff |
| 315 | +# .. |
| 316 | +*.njsproj |
| 317 | +- *.sln |
| 318 | +*.sw? |
| 319 | ++ **/fableoutput/ |
| 320 | +``` |
| 321 | + |
| 322 | +10. Done! 🎉 |
| 323 | + |
| 324 | +F# files will be transpiled to javascript into `src/fableoutput/`. The F# entrypoint is `src/Program.fs` which will be transpiled to `src/fableoutput/Program.fs.jsx` and loaded in `index.html`. |
| 325 | + |
| 326 | +This will be picked up by vite and served to the browser! |
| 327 | + |
| 328 | + |
| 329 | + |
| 330 | + |
0 commit comments