About mcp
mcp is an MCP server published by git+fictjs in the Developer Tools category: mCP server for Fict.
Install
npx @fictjs/mcpclaude mcp add fict -- npx -y @fictjs/mcpnpx -y @fictjs/mcpThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about mcp
Powered by Claude · Grounded in docs
Security checks
- ✓README presentThe repository ships documentation.
- !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.
4 checks have not been run yet.
README
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.
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
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.ts
import { defineConfig } from 'vite'
import fict from '@fictjs/vite-plugin'
export default defineConfig({
plugins: [fict()],
})
{
"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
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))
| 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 |
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
Alpha. Core is stable, but expect edge cases. Test thoroughly for critical apps.
No. Fict compiles to direct DOM operations for surgical, fine-grained updates.
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.
Not directly. Fict compiles to DOM operations, not React elements.
~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.
README copy captured when this server was indexed; it may lag the repository. The original is authoritative.