mcp
MCP server for Fict
Installation
npx @fictjs/mcpAsk AI about mcp
Powered by Claude ยท Grounded in docs
I know everything about mcp. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Fict
Reactive UI with zero boilerplate.
Write JavaScript; let the compiler handle signals, derived values, and DOM updates.
Quick Start ยท Core Concepts ยท Examples ยท Docs ยท Playground
function Counter() {
let count = $state(0)
const doubled = count * 2 // auto-derived
return <button onClick={() => count++}>{doubled}</button>
}
No useMemo. No dependency arrays. No .value. Just JavaScript.
Why Fict?
"Write JavaScript; the compiler handles reactivity." No
.value, no deps arrays, no manual memo wiring. Not pitching "better React/Vue/Svelte" โ Fict is a different mental model: compile-time reactivity on plain JS. The gain: less code, lower cognitive overhead.
| Pain Point | React | Vue 3 | Solid | Svelte 5 | Fict โจ |
|---|---|---|---|---|---|
| State syntax | useState() + setter | ref() + .value | createSignal() + () | $state() | $state() |
| Derived values | useMemo + deps | computed() | createMemo() | $derived() | automatic ๐ฅ |
| Props destructure | โ | โ ๏ธ breaks reactivity | โ breaks reactivity | โ
($props()) | โ |
| Control flow | native JS | v-if/v-for | <Show>/<For> | {#if}/{#each} | native JS |
Fict gives you the best of every world:
- ๐งฉ React's familiar syntax โ JSX, destructuring-friendly, native
if/for - โก Solid's fine-grained updates โ no VDOM, surgical DOM patches
- โจ Less boilerplate than both โ compiler infers derived values automatically
Quick Start
npm install fict
npm install -D @fictjs/vite-plugin # Vite users
๐ฆ Counter App โ full example
import { $state, render } from 'fict'
export function Counter() {
let count = $state(0)
const doubled = count * 2 // auto-derived
return (
<div class="counter">
<h1>Fict Counter</h1>
<div class="card">
<button onClick={() => count--}>-</button>
<span class="count">{count}</span>
<button onClick={() => count++}>+</button>
</div>
<p class="doubled">Doubled: {doubled}</p>
</div>
)
}
render(() => <Counter />, document.getElementById('app')!)
โ๏ธ Vite config
// vite.config.ts
import { defineConfig } from 'vite'
import fict from '@fictjs/vite-plugin'
export default defineConfig({
plugins: [fict()],
})
๐ง TypeScript config
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "fict"
}
}
Online Examples
- ๐ฎ Counter
Core Concepts
$state โ Reactive data
let count = $state(0)
count++ // โ
direct mutation
count = count + 1 // โ
assignment
Automatic derivations โ No useMemo needed
let price = $state(100)
let quantity = $state(2)
const subtotal = price * quantity // auto-derived
const tax = subtotal * 0.1 // auto-derived
const total = subtotal + tax // auto-derived
The compiler builds a dependency graph and only recomputes what's needed.
Single-use derived values may be inlined as an optimization; use $memo or set
inlineDerivedMemos: false to force explicit memo nodes.
$effect โ Side effects
$effect(() => {
console.log(`count is now ${count}`)
return () => {
/* cleanup */
}
})
Execution Model: Not React, Not Solid
This is the most important concept to understand.
function Counter() {
console.log('A') // ๐ต Runs ONCE
let count = $state(0)
const doubled = count * 2
console.log('B', doubled) // ๐ข Runs on EVERY count change
return (
<button onClick={() => count++}>
{(console.log('C'), doubled)} {/* ๐ข Runs on every change */}
{(console.log('D'), 'static')} {/* ๐ต Runs ONCE */}
</button>
)
}
| Phase | Output | Why |
|---|---|---|
| Initial render | A โ B 0 โ C โ D | Everything runs once |
| After click (count: 0โ1) | B 2 โ C | A and D don't run! |
The mental model
| Framework | What happens on state change |
|---|---|
| React | Entire component function re-runs |
| Solid | Component runs once; you manually wrap derived values |
| Fict | Component runs once; code depending on state auto-recomputes |
Fict splits your component into reactive regions:
- ๐ต Code before
$state: runs once - ๐ข Expressions using state (
count * 2): recompute when dependencies change - ๐ต Static JSX: runs once
Examples
Conditional rendering
function App() {
let show = $state(true)
return (
<div>
{show && <Modal />}
{show ? <A /> : <B />}
</div>
)
}
No <Show> or {#if} โ just JavaScript.
List rendering
function TodoList() {
let todos = $state([
{ id: 1, text: 'Learn Fict' },
{ id: 2, text: 'Build something' },
])
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
)
}
No <For> or v-for โ just .map().
Async data fetching
function UserProfile({ userId }: { userId: string }) {
let user = $state<User | null>(null)
let loading = $state(true)
$effect(() => {
const controller = new AbortController()
loading = true
fetch(`/api/user/${userId}`, { signal: controller.signal })
.then(res => res.json())
.then(data => {
user = data
loading = false
})
return () => controller.abort() // cleanup on userId change
})
if (loading) return <Spinner />
return <div>{user?.name}</div>
}
Props stay reactive
function Greeting({ name, age = 18 }: { name: string; age?: number }) {
const label = `${name} (${age})` // auto-derived from props
return <span>{label}</span>
}
Destructuring works. No toRefs() or special handling needed.
What Fict Compiles To
// โ๏ธ Your code
function Counter() {
let count = $state(0)
const doubled = count * 2
return <div>{doubled}</div>
}
// โก Compiled output (simplified)
function Counter() {
const count = createSignal(0)
const doubled = createMemo(() => count() * 2)
const div = document.createElement('div')
createEffect(() => {
div.textContent = doubled()
})
return div
}
You write the simple version. The compiler generates the efficient version.
Advanced Features
Error Boundaries
import { ErrorBoundary } from 'fict'
;<ErrorBoundary fallback={err => <p>Error: {String(err)}</p>}>
<RiskyComponent />
</ErrorBoundary>
Suspense
import { Suspense } from 'fict'
import { resource, lazy } from 'fict/plus'
const userResource = resource({
suspense: true,
fetch: (_, id: number) => fetch(`/api/user/${id}`).then(r => r.json()),
})
const LazyChart = lazy(() => import('./Chart'))
function Profile({ id }) {
return (
<Suspense fallback="Loading...">
<h1>{userResource.read(() => id).data?.name}</h1>
<LazyChart />
</Suspense>
)
}
SSR Streaming
Fict SSR supports shell-first streaming with Suspense boundary patching:
import { renderToPipeableStream } from '@fictjs/ssr'
const { pipe, shellReady, allReady } = renderToPipeableStream(() => <App />, {
mode: 'shell',
})
pipe(res)
await shellReady
await allReady
๐งช Partial prerendering (Preview)
import { renderToPartial } from '@fictjs/ssr'
const { shell, stream } = renderToPartial(() => <App />, { mode: 'shell' })
// shell: complete fallback HTML
// stream: deferred boundary patches
renderToPartial is an advanced API (Preview in v1.0).
fict/plus โ Advanced APIs
import { $store, untrack } from 'fict'
import { resource, lazy } from 'fict/plus'
// Deep reactivity with path-level tracking
const user = $store({ name: 'Alice', address: { city: 'London' } })
user.address.city = 'Paris' // fine-grained update
// Derived values are auto-memoized, just like $state
const greeting = `Hello, ${user.name}` // auto-derived
// Method chains are also auto-memoized
const store = $store({ items: [1, 2, 3, 4, 5] })
const doubled = store.items.filter(n => n > 2).map(n => n * 2) // auto-memoized
// Dynamic property access works with runtime tracking
const value = store[props.key] // reactive, updates when key or store changes
// Escape hatch for black-box functions
const result = untrack(() => externalLib.compute(count))
๐ $store vs $state
| Feature | $state | $store |
|---|---|---|
| Depth | Shallow | Deep (nested objects) |
| Access | Direct value | Proxy-based |
| Mutations | Reassignment | Direct property mutation |
| Derived values | Auto-memoized | Auto-memoized |
| Best for | Primitives, simple objects | Complex nested state |
Control Flow and Branch Reactivity
Fict components execute once on mount. Reactive updates happen through bindings/memos.
JSX-only reads โ fine-grained DOM updates:
let count = $state(0)
return <div>{count}</div> // Only the text node updates
Control flow returns โ compiler emits reactive branch bindings:
let count = $state(0)
if (count > 10) return <Special /> // branch swaps reactively when count changes
return <Normal />
The compiler detects supported patterns (if-return, switch-return, try blocks containing
return branches) and lowers them to reactive conditionals.
Framework Comparison
| Feature | React+Compiler | Solid | Svelte 5 | Vue 3 | Fict โจ |
|---|---|---|---|---|---|
| State syntax | useState() | createSignal() | $state() | ref() | $state() |
| Read state | count | count() | count | count.value | count |
| Update state | setCount(n) | setCount(n) | count = n | count.value = n | count = n |
| Derived values | auto | createMemo() | $derived() | computed() | auto |
| Props destructure | โ | โ | via $props() | via toRefs() | โ |
| Control flow | native JS | <Show>/<For> | {#if}/{#each} | v-if/v-for | native JS |
| File format | .jsx/.tsx | .jsx/.tsx | .svelte | .vue | .jsx/.tsx |
| Rendering | VDOM | fine-grained | fine-grained | fine-grained | fine-grained |
Performance
๐ง Note: Bundle size and memory optimizations are currently in progress.

Benchmark Summary (js-framework-benchmark)
| Benchmark | Vue Vapor | Solid | Svelte 5 | Fict | React Compiler |
|---|---|---|---|---|---|
| create rows (1k) | 24.5ms | 24.5 | 24.5 | 26.2 | 29.3 |
| replace all rows (1k) | 28.1 | 28.0 | 29.1 | 30.7 | 34.8 |
| partial update (10th row) | 14.7 | 15.0 | 15.3 | 15.3 | 18.6 |
| select row | 3.4 | 4.2 | 6.2 | 3.6 | 10.3 |
| swap rows | 17.4 | 17.7 | 17.2 | 17.1 | 115.6 |
| remove row | 11.5 | 11.4 | 11.8 | 12.0 | 13.9 |
| create many rows (10k) | 263.7 | 256.8 | 264.6 | 270.7 | 398.2 |
| append rows (1k to 1k) | 29.7 | 29.0 | 29.2 | 30.4 | 35.5 |
| clear rows (1k) | 11.8 | 15.1 | 13.5 | 14.3 | 21.6 |
| Geometric Mean | 1.01 | 1.04 | 1.06 | 1.07 | 1.45 |
Lower is better. Geometric mean is the weighted mean of all relative factors.
Versions: Vue Vapor 3.6.0-alpha.2 ยท Solid 1.9.3 ยท Svelte 5.42.1 ยท React Compiler 19.0.0
Status & Roadmap
โ ๏ธ Alpha โ Fict is feature-complete for core compiler and runtime. API is stable, but edge cases may be refined. Don't use it in production yet.
โ Completed
- Compiler with HIR/SSA
- Stable
$state/$effectsemantics - Automatic derived value inference
-
$storeinfict,resource/lazyinfict/plus -
startTransition,useTransition,useDeferredValueinfict - Vite plugin
- ESLint plugin
- Support sourcemap
- DevTools
- Router
- Testing library
- SSR / streaming
๐บ๏ธ Planned
- Migration guides from React/Vue/Svelte/Solid
Documentation
| Doc | Description |
|---|---|
| Architecture | How the compiler and runtime work |
| API Reference | Complete API documentation |
| Compiler Spec | Formal semantics |
| ESLint Rules | Linting configuration |
| Diagnostic Codes | Compiler warnings reference |
| Config Profiles | Recommended dev/CI/prod settings |
| Cycle Protection | Dev-mode infinite loop detection |
| SSR SEO Guide | SEO best practices for SSR pages |
| SSR Performance | Snapshot size & render-mode tuning |
| SSR Deployment | Vercel/Cloudflare/edge deployment |
| DevTools | Vite plugin usage & auto-injection |
๐ Linting & diagnostics
Install @fictjs/eslint-plugin and extend plugin:fict/recommended:
{
"plugins": ["fict"],
"extends": ["plugin:fict/recommended"]
}
Key rules: nested component definitions (FICT-C003), missing list keys (FICT-J002), memo side effects (FICT-M003), empty $effect (FICT-E001), component return checks (FICT-C004), plus $state placement/alias footguns.
- Recommended config mirrors compiler warnings so IDE diagnostics stay aligned with build output.
- For strict CI gates, enable compiler
strictReactivity: trueto escalate control-flow fallback diagnostics (FICT-R003,FICT-R006) to build errors. strictGuaranteeis enabled by default for fail-closed guarantees.- Production compilation (
NODE_ENV=production) force-enablesstrictGuaranteeeven when an integration opts out. - Set
strictGuarantee: falseonly for non-production migration or benchmark builds. - CI can force strict mode with
FICT_STRICT_GUARANTEE=1during build steps. - Guarantee boundary reference:
docs/reactivity-guarantee-matrix.md.
FAQ
Is Fict production-ready?
Alpha. Core is stable, but expect edge cases. Test thoroughly for critical apps.
Does Fict use a virtual DOM?
No. Fict compiles to direct DOM operations for surgical, fine-grained updates.
How does Fict handle arrays?
Default: immutable style (todos = [...todos, newTodo]). For deep mutations, use spread to create new immutable data, or use Immer/Mutative, or use $store from fict.
Can I use existing React components?
Not directly. Fict compiles to DOM operations, not React elements.
How big is the runtime?
~10kb brotli compressed. Performance is within ~3% of Solid in js-framework-benchmark (geometric mean 1.07 vs 1.04).
Known Limitations
The compiler has some limitations when handling conditional rendering patterns.
Control-flow patterns supported
- Multiple sequential
if-returnbranches are compiled into reactive conditionals. ifblocks withoutreturnare auto-wrapped so reactive side effects still update.- Nested branch logic (e.g. inner
if/switch) and reactive prelude reads beforereturnare automatically kept reactive. When fine-grained lowering is not possible, the compiler enables a safe runtime fallback that tracks branch reads and re-runs the active branch.
Acknowledgments
Fict is built upon the brilliant ideas and relentless innovation of the open-source community. We express our deepest respect and gratitude to these projects:
- React โ For defining the modern era of UI development. Its component model and declarative philosophy set the standard for developer experience.
- Solid โ For pioneering fine-grained reactivity and demonstrating the power of compilation. Its architecture is the bedrock upon which Fict's performance is built.
- Qwik โ For its outstanding resumability-first SSR vision. Its approach to instant interactivity has been a major inspiration for Fict's resumable SSR direction.
- alien-signals โ For pushing the boundaries of signal performance. Its implementation provided critical guidance for Fict's reactive system.
MIT License ยท ยฉ Fict Contributors
