TypeScript and React for ASP.NET Core, with a Vite-style dev loop and no Node.js toolchain

Build the frontend and the backend in one project, one process, one dotnet watch. Components update in place with their state intact; C# changes re-fetch without a reload; dotnet publish ships hashed, code-split bundles.

$ nite init my-app
$ cd my-app && nite watch
# edit Client/App.tsx: updated in ~150 ms, state kept
# edit Program.cs: the page re-fetches, no reload
$ dotnet publish -c Release

One project. One process. No node_modules rituals.

NiteFx does in .NET what Vite does in Node: it compiles your client code on request, serves dependencies through an import map, and pushes hot updates over a WebSocket. esbuild does the compiling; NiteFx does everything around it.

React Fast Refresh

Edit a component and it updates in place in 100 to 200 ms, keeping its state. Helpers refresh the components that use them; the entry module reloads the page.

Hot reload →

Full-stack hot reload

nite watch runs dotnet watch. C# edits apply in the running app and raise a nite:server-update event, so the page re-fetches without losing state.

C# changes →

Single-page apps

Client/index.html plus React, served by app.MapNiteSpa() next to your API. Same origin: no CORS, no dev proxy.

SPA guide →

Islands in Razor

Server-rendered pages with interactive React exactly where you need it: <island name="Chart" client="visible" />.

Islands guide →

npm packages, from C#

A small npm client resolves semver, writes a lockfile and fills a normal node_modules. Private registries work from your .npmrc.

Packages →

Sass, Less, Tailwind, PostCSS

The same stylesheet pipeline as Vite, CSS modules included. Tailwind finds classes in your Razor views too. Mantine works out of the box.

Stylesheets →

Looks like ASP.NET Core, because it is

Three calls in Program.cs, and a Client folder next to it.

Program.cs

using Nite;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddNite();

var app = builder.Build();
app.UseNite();   // dev server under /_nite

app.MapGet("/api/todos", () => Todos.All());

app.MapStaticAssets();
app.MapNiteSpa(); // index.html for client routes
app.Run();

Client/App.tsx

import { useEffect, useState } from "react";

export function App() {
  const [todos, setTodos] = useState([]);

  useEffect(() => {
    const load = async () =>
      setTodos(await (await fetch("/api/todos")).json());
    load();
    // C# hot reload: re-fetch, keep state
    addEventListener("nite:server-update", load);
  }, []);

  return <TodoList items={todos} />;
}

Already have a Vite app?

Bring it across in one command. NiteFx reads your lockfile (npm, Yarn, pnpm or Bun) and keeps the exact versions you run today.

Import

nite init my-app --from ../vite-app copies the client, keeps your dependencies and stylesheet tooling, and tells you what it could not bring along.

Importing projects →

Ship

dotnet publish runs the production build: minified, code-split, content-hashed bundles in wwwroot/_nite, served precompressed by MapStaticAssets.

Deployment →

Understand it

No magic: a long-lived esbuild service, an import map, a module graph and a WebSocket. Read how the pieces fit before you bet a project on it.

How NiteFx works →

Status

NiteFx is young (version 0.8), and one dotnet tool install away. It is honest about what it does not do: read Limits and non-goals before you start.