Build faster. Understand what ships.
Documentation · Why Foldkit · Examples · Get Started · Discord
Foldkit is a TypeScript frontend framework built on Effect. It makes state and side effects explicit, so your team and AI agents can build features, trace behavior, and test changes.
React, Vue, Svelte, and Solid solve rendering and leave the architecture to you. Foldkit gives you the architecture, so you can focus on your domain.
Foldkit uses The Elm Architecture. Application state does not live in component instances or hook lifecycles. The Model is the single source of truth, and every transition stays visible in update. That discipline is a real commitment. Foldkit works best when the team wants one architecture across the application and is ready to build on Effect throughout. Coming from React?
Note
Foldkit is pre-1.0 and under active development. The architecture is settled and the core API is stable in practice, but breaking changes may occur in minor releases. See the roadmap.
Get Started
Scaffold a browser-only app, a statically generated site, or a server-rendered app with create-foldkit-app:
npx create-foldkit-app@latestThe Get Started guide covers the generated project and how to run it.
Counter
This complete program defines the Model, Messages, update, init, and view in main.ts. entry.ts starts the Runtime separately, so tests can import the program without booting it.
// src/main.ts
import { Schema } from 'effect'
import { Runtime, Update } from 'foldkit'
import { Document, HtmlBuilder } from 'foldkit/html'
import { defineMessageUnion } from 'foldkit/message'
import { modifyFields } from 'foldkit/struct'
// MODEL
export const Model = Schema.Struct({ count: Schema.Number })
export type Model = typeof Model.Type
// MESSAGE
export const Message = defineMessageUnion({
ClickedDecrement: {},
ClickedIncrement: {},
ClickedReset: {},
})
export type Message = typeof Message.Type
// UPDATE
export const update = (model: Model, message: Message) =>
Message.match<Update.Return<Model, Message>>(message, {
ClickedDecrement: () => ({
model: modifyFields(model, { count: count => count - 1 }),
}),
ClickedIncrement: () => ({
model: modifyFields(model, { count: count => count + 1 }),
}),
ClickedReset: () => ({ model: modifyFields(model, { count: () => 0 }) }),
})
// INIT
export const init: Runtime.ApplicationInit<Model, Message> = () => ({
model: { count: 0 },
})
// VIEW
export const view = (model: Model, h: HtmlBuilder<Message>): Document => ({
title: `Counter: ${model.count}`,
body: h.div(
[],
[
h.p([], [model.count.toString()]),
h.button([h.OnClick(Message.ClickedDecrement())], ['-']),
h.button([h.OnClick(Message.ClickedReset())], ['Reset']),
h.button([h.OnClick(Message.ClickedIncrement())], ['+']),
],
),
})// src/entry.ts
import { Runtime } from 'foldkit'
import { Model, init, update, view } from './main'
const application = Runtime.makeApplication({
Model,
init,
update,
view,
container: document.getElementById('root'),
})
Runtime.run(application)Try Counter in the browser, follow the walkthrough, or explore the source.
What Ships With Foldkit
- Handle side effects: Use Commands for one-time work, Subscriptions for external events, Mounts for element lifecycles, and ManagedResources for stateful handles.
- Build pages and embedded apps: Add routing, server rendering, or embedding.
- Grow an interface: Compose Submodels and UI components.
- Test and debug behavior: Use Story and Scene testing and DevTools to trace state changes.
- Inspect an app with AI agents: DevTools MCP exposes the running Model and Message history.
Examples
- Form: validation with an async email check
- Auth: Submodels and OutMessage
- WebSocket Chat: a ManagedResource for a live connection
- Pixel Art: interactive editor with keyboard shortcuts and persistence
- Typing Game: multiplayer game with an Effect RPC backend (play it live)
Explore the full example gallery.
Development
See CONTRIBUTING.md to work on Foldkit and AGENTS.md for code conventions. Community projects can use the Foldkit name and logo under the branding guidelines.
License
MIT