The front end framework for correctness: built on Effect, architected like Elm
Foldkit: A Frontend Framework Engineered for Correctness
The Mission: To provide a TypeScript frontend environment where correctness is the default, leveraging the power of Effect-TS and the structural integrity of the Elm Architecture.
npx create-foldkit-app@latest
🏗️ Architecture Over Rendering
While popular libraries like React, Vue, Svelte, and Solid focus primarily on the how of rendering, they leave the what of application architecture entirely up to the developer. This often leads to fragmented patterns and "spaghetti" state.
Foldkit flips this script. It provides the full architectural blueprint, allowing you to dedicate your mental energy to your actual business domain rather than debating state management patterns.
The Core Philosophy
| Feature | Traditional Frameworks | Foldkit |
|---|---|---|
| State | Distributed/Local/Global (Mutable/Immutable) | Single Immutable Model |
| Side Effects | Imperative calls inside handlers/hooks | Explicit values returned from update |
| Scaling | Complexity often grows exponentially | Complexity grows linearly |
| Guidance | Unopinionated (Bring your own architecture) | Prescriptive (The Elm Architecture) |
🛠️ Technical Foundations
Every Foldkit application is fundamentally an Effect. The state is defined by a single Schema, and side effects are modeled as Effects that are guaranteed not to fail.
The TEA Loop
The flow of data in Foldkit follows a strict unidirectional cycle:
Mathematically, the state transition can be viewed as:
Predictability at Scale
Complexity is inevitable. Complexity is manageable. Whether your app consists of 5 files or 50, the patterns remain identical. New features add structure rather than entanglement.
💻 Implementation Example: Async Counter
Below is a conceptual look at how behavior is declared in Foldkit.
// MESSAGE DEFINITIONS
const ClickedIncrement = m('ClickedIncrement');
const ChangedResetDuration = m('ChangedResetDuration', { seconds: S.Number });
const ClickedResetAfterDelay = m('ClickedResetAfterDelay');
const CompletedDelayReset = m('CompletedDelayReset');
// COMMANDS
const DelayReset = Command.tagsExhaustive({
ClickedIncrement: () => [evo(model, { count: model.count + 1 }), []],
ChangedResetDuration: ({ seconds }) => [evo(model, { resetDuration: seconds }), []],
ClickedResetAfterDelay: () => [
evo(model, { isResetting: true }),
[DelayReset({ seconds: model.resetDuration })]
],
CompletedDelayReset: () => [evo(model, { count: 0, isResetting: false }), []],
});
📦 Batteries Included
Foldkit doesn't make you hunt for a dozen different libraries. It provides a cohesive suite of built-in tools:
- Typed Routing: URLs parse into types and rebuild back into URLs. No more string-matching errors.
- Accessible UI: A library of components (Dialogs, Tabs, Menus, etc.) designed specifically for the Elm Architecture.
- Submodels: Encapsulate a Model, Message, Update, and View within a larger program. Children communicate via typed
OutMessages. - Subscriptions: Bind Model slices to scoped
Streams. The runtime manages the lifecycle automatically. - Managed Resources: Model-driven lifecycles for complex browser APIs like
WebSocketsorRTCPeerConnection. - Field Validation: Built-in support for synchronous and asynchronous predicates tracked within the Model.
🧪 Testing & Observability
Dual-Layer Testing
Foldkit separates logic testing from visual testing:
- Story: Tests the state machine. It resolves a message and asserts on the resulting
ModelandCommands. - Scene: Tests the user experience. It interacts with the rendered HTML using accessible locators.
import { Scene, Story } from 'foldkit';
import { expect, test } from 'vitest';
// Story: Testing the state transition
test('fetch weather updates the model', () => {
Story.resolve(FetchWeather, SucceededFetchWeather({ weather }));
Story.toBe('WeatherSuccess');
});
// Scene: Testing the end-to-end view
test('type zip, click get weather, see forecast', () => {
Scene.resolve(FetchWeather, SucceededFetchWeather({ weather }));
});
Time-Traveling DevTools
Because every change flows through a single Model and a history of Messages, Foldkit enables DevTools that are impossible in mutable frameworks. You can:
- Inspect the current
Modelstate. - Walk through the
Messagehistory. - Rewind the UI to any previous state.
🤖 AI-Ready Architecture
The prescriptive nature of Foldkit makes it a perfect partner for LLMs:
- Generation: AI agents excel at generating Foldkit code because the patterns are consistent.
- Review: Humans can review AI-generated code easily due to the lack of hidden side effects.
- MCP Integration: AI agents can connect via the Model Context Protocol to read the Model, inspect history, and dispatch Messages directly.
🎯 Who is Foldkit For?
Foldkit requires a shift in mindset. There are no hooks, no local component state, and no hidden effects. It is designed for:
- Effect Developers: If your backend is already built on
Effect-TS, Foldkit provides a seamless, zero-context-switch frontend. - Correctness Seekers: For those who want an architecture that prevents bugs by design.
- Aligned Teams: Teams that want a single, unified pattern to accelerate onboarding and reduce architectural disputes.
- Complex State Projects: Ideal for multi-step forms, real-time data dashboards, and intricate authentication flows where explicit transitions are vital.