Quick start

The fastest way to a running app is the create-fluixi scaffolder:

npm create fluixi@latest my-app
# or: pnpm create fluixi my-app · yarn create fluixi my-app

The scaffolder asks the following questions in order:

  • App mode, SSR (server-rendered via @fluixi/start) or SPA (client-only via Vite)
  • Template syntax, JSX (default) or html`` (plain .ts files, no JSX)
  • Component library, none or @fluixi-ui (kit and tokens; components installed as needed)
  • Styling, plain CSS, Tailwind CSS v4, or @fluixi-css
  • Authentication, none, OAuth2/OIDC (identity provider, tokens in browser or on server), or Better Auth (SSR apps only)

Then:

cd my-app
npm install
npm run dev

New apps start on Vite 8 and TypeScript 7. Vite 8 needs Node 20.19 or later, or 22.12 or later.

You can also skip the prompts:

npm create fluixi@latest my-app -- --ssr --jsx    # or --spa / --html
npm create fluixi@latest my-app -- --ssr --auth=oauth2 --provider=keycloak --tokens=server

Each question is represented by a flag, so any question answered on the command line is not asked again. Authentication flags include --auth=oauth2, --auth=better-auth, or --no-auth; when using oauth2 you can also give --provider and --tokens. Google and Dex store tokens on the server, therefore --tokens=browser is refused with those providers, and an SSR app is required for authentication, so --spa combined with --auth is refused.

JSX or html``?

Fluixi compiles both JSX and tagged html `` templates to the same fine-grained DOM calls. Pick whichever you prefer, or mix them in one file.

// JSX, needs `jsx` configured in tsconfig
function Counter() {
  const n = $signal(0);
  return <button onClick={() => n.set(n() + 1)}>{n()}</button>;
}
// html``, plain .ts, no jsx in tsconfig
import { html } from '@fluixi/core';

function Counter() {
  const n = $signal(0);
  return html`<button @click=${() => n.set(n() + 1)}>${n()}</button>`;
}

Choose html`` when you don't want to configure JSX (for example a library, a script, or a plain-TypeScript codebase). See html`` templates for the full syntax.

Every scaffolded app includes @fluixi/ts-plugin, which gives html `` templates real TypeScript support in your editor, hover, completion and type-checking inside the template. It runs in the editor's built-in TypeScript, also for a project on TypeScript 7: see Editor support.

Add a component or route

The CLI scaffolds pieces into an existing app, matching your project's syntax automatically:

npx fluixi generate component Button    # → src/components/Button.tsx (or .ts for html``)
npx fluixi generate route about         # → src/routes/about.tsx

Add authentication

npx fluixi add auth                                              # asks
npx fluixi add auth oauth2 --provider keycloak --tokens server
npx fluixi add auth better-auth --no-install

The command asks whether you want OAuth2/OIDC or Better Auth, then prompts for the OAuth2 provider and token location unless flags answer them, with the same flags as create-fluixi. It writes the same files that create-fluixi creates for a new app, preserving any existing files and printing lines to add to uneditable configs. The package is installed with the project's package manager, unless --no-install is used. Then copy .env.example to .env and register the redirect URI with the provider. See the documentation for OAuth2 and Better Auth.

Starting from scratch

If you'd rather wire it up yourself, install the runtime and the Vite plugin:

npm install @fluixi/core
npm install -D @fluixi/vite-plugin vite
// vite.config.ts
import { defineConfig } from 'vite';
import { fluixi } from '@fluixi/vite-plugin';

export default defineConfig({ plugins: [fluixi()] });
// src/main.ts
import { render, html } from '@fluixi/core';
import { App } from './app';

render(() => html`<${App} />`, document.getElementById('app')!);

Which build am I running?

Whichever way you mount, render from @fluixi/core, or hydration through startClient, the mount element is stamped with the versions actually running, and they are exposed as window.Fluixi:

<div id="app" fluixi="1.0.0" fx-dom="1.0.0" fx-reactive="1.0.0">
> window.Fluixi
{ core: '1.0.0', dom: '1.0.0', reactive: '1.0.0' }

These three ship as one release. If they disagree, the console says so, a mismatch means a stale lockfile or two copies resolved side by side, which otherwise surfaces much later as reactivity that makes no sense.

Continue to Signals for the reactive core, or jump to html`` templates.

Released under the MIT License.

Copyright © 2026 Ibrahima Touré