Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) from version [0.1.0] moving forward.

## [0.3.1] - 2026-08-29

### Changed

- The README is a getting started guide: requirements, install, the minimal `vite.config.js`, how to import an F# entry point from `index.html`, and the React setup, with everything else linked to the documentation site. It used to describe the package as a name reservation on npm and point readers at the source. The plugin options are linked rather than repeated, so the table has one home.

### Fixed

- The package has a `description` and `keywords`. Both were empty, so npm derived the description from the first line of the README and listed the version badge's Markdown as the package summary.

## [0.3.0] - 2026-08-29

### Changed
Expand Down
90 changes: 81 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,96 @@
> [!IMPORTANT]
> This project is up for adoption. I'm looking for eager people to maintain this.<br>Please open a [discussion](https://github.com/fable-compiler/vite-plugin-fable/discussions) if you are interested!

## What is this?
Compile [F#](https://fsharp.org/) with [Fable](https://fable.io/) from inside [Vite](https://vite.dev/), so a `.fs` file is just another module Vite can import.

Alright, the `tl;dr` is that I don't like the current way of how you can use [Fable](https://fable.io) with [Vite](https://vitejs.dev).
I'm referring to the steps in the [get started with Vite](https://fable.io/docs/getting-started/javascript.html#browser), I don't like it and have an alternate take on it.
The usual setup puts Fable in front of your dev server (`dotnet fable watch --run vite`). This plugin does not. You run `vite`, and F# is compiled on demand and updated over HMR, the same way Vite treats TypeScript, JSX or Sass.

More thoughts on this can be read from the [documentation](https://fable.io/vite-plugin-fable/).
## Requirements

## Current status
- The **.NET 10 SDK** on your `PATH`. Reading your `.fsproj` means asking MSBuild about it. Check with `dotnet --version`.
- **Vite 8** (peer dependency).

A first package was pushed to npm. This was merely to reserve the package name.
You can read the code, that's is it for now.
You do not need Fable as a dotnet tool. The compiler ships prebuilt inside the package, so there is no post-install step and installing with `--ignore-scripts` is fine.

### Recent Notes
## Install

Support for the latest .NET runtime was added in [v0.1.1](https://github.com/fable-compiler/vite-plugin-fable/blob/main/CHANGELOG.md#011---2025-06-03). Please upgrade to the latest version. Earlier versions may fail silently if the .NET 8 runtime is missing—see the changelog for details.
```bash
npm install -D vite-plugin-fable
bun install -D vite-plugin-fable
```

## Getting started

Add the plugin to your Vite config:

```js
// vite.config.js
import { defineConfig } from "vite";
import fable from "vite-plugin-fable";

export default defineConfig({
plugins: [fable()],
});
```

The plugin compiles the single `.fsproj` next to your Vite config. If there is more than one, point at the one you want with `fable({ fsproj: "./src/App.fsproj" })`.

Import your F# entry point as a module:

```html
<script type="module">
import "/App.fs";
</script>
```

`<script type="module" src="/App.fs">` does not work: Vite only resolves the `.fs` extension inside module resolution. See [vitejs/vite#9981](https://github.com/vitejs/vite/pull/9981).

Now start Vite. The plugin stays quiet and prints one line per compile:

```text
VITE v8.2.2 ready in 376 ms

➜ Local: http://localhost:5173/
12:32:44 PM [vite] [fable] compiled App.fsproj in 1.53s
```

### With React

The most common setup, using [Fable.Core.JSX](https://fable.io/blog/2022/2022-10-12-react-jsx.html):

```js
// vite.config.js
import { defineConfig } from "vite";
import fable from "vite-plugin-fable";
import react from "@vitejs/plugin-react";

export default defineConfig({
plugins: [fable({ jsx: "automatic" }), react({ include: /\.fs$/ })],
});
```

Two things to know about that line:

- **Order matters.** `fable` goes before `react`.
- **The two options do different jobs.** `fable({ jsx })` is what turns Fable's JSX into JavaScript; Vite cannot do it for a `.fs` module. `react({ include: /\.fs$/ })` is what makes `.fs` components Fast Refresh boundaries, so an edit updates in place instead of reloading the page.

Using [Feliz.CompilerPlugins](https://www.nuget.org/packages/Feliz.CompilerPlugins) or plain [Fable.React](https://www.nuget.org/packages/Fable.React) instead? Those emit classic-runtime React and need a different `react()` filter. The [recipes](https://fable.io/vite-plugin-fable/recipes.html) page has both, plus the React Compiler.

## Documentation

- [Getting started](https://fable.io/vite-plugin-fable/getting-started.html)
- [Plugin options](https://fable.io/vite-plugin-fable/recipes.html#Plugin-options), every option the plugin accepts
- [Recipes](https://fable.io/vite-plugin-fable/recipes.html), including React, an alternative `fsproj`, and Debug versus Release
- [Debugging](https://fable.io/vite-plugin-fable/debug.html), what the plugin prints and how to ask the daemon what it is doing
- [How does this work?](https://fable.io/vite-plugin-fable/how.html)
- [Changelog](https://github.com/fable-compiler/vite-plugin-fable/blob/main/CHANGELOG.md)

## Video

I talked a little bit about this project during this stream:

[![vite-plugin-fable stream](http://img.youtube.com/vi/nVpUaVFNpMk/maxresdefault.jpg)](https://youtu.be/mnqwwtSQfRU?si=VpDDv3SzHikXL5iu&t=141 "vite-plugin-fable")

## License

[Apache-2.0](https://github.com/fable-compiler/vite-plugin-fable/blob/main/LICENSE)
6 changes: 3 additions & 3 deletions packages/vite-plugin-fable/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "vite-plugin-fable",
"version": "0.2.1",
"description": "",
"keywords": [],
"version": "0.0.0-changelog",
"description": "Compile F# with Fable from inside Vite, so a .fs file is just another module Vite can import.",
"keywords": ["vite", "vite-plugin", "fable", "fsharp", "f#", "dotnet", "hmr"],
"homepage": "http://fable.io/vite-plugin-fable/",
"bugs": "https://github.com/fable-compiler/vite-plugin-fable/issues",
"license": "Apache-2.0",
Expand Down
Loading