Skip to content

Latest commit

 

History

268 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vite plugin for Fable

NPM Version

vite-plugin-fable logo

Important

This project is up for adoption. I'm looking for eager people to maintain this.
Please open a discussion if you are interested!

Compile F# with Fable from inside Vite, so a .fs file is just another module Vite can import.

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.

Requirements

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

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.

Install

npm install -D vite-plugin-fable
bun install -D vite-plugin-fable

Getting started

Add the plugin to your Vite config:

// 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:

<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.

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

  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:

// 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 or plain Fable.React instead? Those emit classic-runtime React and need a different react() filter. The recipes page has both, plus the React Compiler.

Documentation

Video

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

vite-plugin-fable stream

License

Apache-2.0

About

A Vite plugin for Fable

Topics

Resources

Stars

44 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages