
Foldkit 0.154.0 and 0.155.0
August 29, 2026 · Devin Jameson
Foldkit 0.155.0 is out. I never announced 0.154.0, so this post covers both.
0.154.0 shipped earlier this week with several additions worth calling out:
h.OnClickgaineddefaultAction,propagation, andfocusSelectorcontrols. Foldkit applies them synchronously before dispatching the Message, covering browser behavior such as preventing a default action, stopping DOM propagation, and focusing an existing element inside the originating gesture.h.OnClickFocuswas deprecated in 0.154.0 and is removed in 0.155.0. Thank you to @hdoro for pointing out the limitation inh.OnClick!h.OnFocusEnterandh.OnFocusLeavemodel focus across a compound region. Put them on a common ancestor and Foldkit dispatches only when focus crosses that boundary, not when it moves between descendants.Update.foldChildandUpdate.foldChildStepcan now emit a derived parent OutMessage fromfoldOutMessage. A child fact can update the current Submodel and produce a different fact for its parent without making the parent Message handler inspect the child Message again.Popover now supplies a headless
arrowattribute bundle and keeps it positioned as the panel flips and shifts. You draw the arrow; Foldkit publishes the placement and offsets. Thank you to @wmaurer for contributing it!@foldkit/oxlint-plugingainedfoldkit/no-empty-to-parent-out-message, which catchestoParentOutMessagemappers that always returnundefinedand should be omitted.
Now for 0.155.0.
Foldkit gained server rendering two weeks ago. It worked, but building a server-rendered application still happened across three commands: build the browser bundle, build the server bundle, then prerender the configured routes.
That separation mattered as soon as another Vite plugin needed to participate in the build. A deployment adapter or host plugin could join the browser build and never see the server build that followed in a separate process. It could deploy half an application. The browser and server builds could also mint different build ids, producing HTML that hydration correctly refused.
@foldkit/vite-plugin 0.20.0 now owns the whole build:
foldkit({
buildId,
ssr: {
serverEntry: '/src/entry.server.ts',
build: { prerender: true },
},
})Run vite build once. Vite produces the browser bundle, the server bundle, and, when prerender is enabled, a page for every path exported by the server entry. Every environment belongs to the same build and shares the same build id.
The build also writes foldkit.build.json beside the server bundle. It records the output directories, server entry, and generated paths so a deployment host can derive its routing behavior from the artifact instead of asking you to repeat it in platform settings.
Applications without ssr.build still produce a browser bundle exactly as before.
This work came from @filipfalcon. Thank you!
Mount.define and Mount.defineStream used to take positional inputs, then accept their work in a second call. A Mount with args had a curried args => element => Effect<Message> body.
It looked compact, but it hid a bad boundary: the outer function ran when view constructed the MountAction. Anything written between those arrows ran during a pure view, on every render, rather than when the element entered the DOM.
Mount definitions now use one config object with named args, messages, and execute fields:
Before:
const AnchorPopover = Mount.define(
'AnchorPopover',
{ buttonId: S.String, anchor: AnchorConfig },
CompletedAnchorPopover,
)(
({ buttonId, anchor }) =>
element =>
Effect.gen(function* () {
// ...
}),
)After:
const AnchorPopover = Mount.define('AnchorPopover', {
args: { buttonId: S.String, anchor: AnchorConfig },
messages: [CompletedAnchorPopover],
execute: ({ element, buttonId, anchor }) =>
Effect.gen(function* () {
// ...
}),
})execute has the same flat shape with or without args. Constructing a MountAction now runs nothing. The runtime calls execute only after the element exists.
This is a breaking change, and @foldkit/ui 0.155.0 now requires foldkit 0.155.0 because its Mount definitions use the new shape. The Mount docs have the complete migration.
Thank you to @hdoro for suggesting this API change!
@foldkit/ui now includes HoverIntent, a behavior-only Submodel for interactions that reveal a panel from a trigger.
It opens after a configurable pointer delay, stays open while pointer or focus moves between the trigger and panel, and closes after a configurable grace period. Focus opens immediately. Escape closes immediately and suppresses reopening until the interaction fully disengages.
HoverIntent returns headless trigger and panel event bundles. It does not create markup, choose an ARIA role, position the panel, or style anything. A Hover Card can pair it with Anchor. A Navigation Menu can coordinate several HoverIntent Models around one shared viewport. The behavior is reusable because those product decisions remain yours.
The new HoverIntent docs include live Hover Card and Navigation Menu examples.
Thank you to @SyahrulBhudiF for contributing it!
There is plenty more in 0.155.0:
FieldValidation.matchexhaustively matchesNotValidated,Validating,Valid, andInvalidfield states without a manual Effect Match pipeline.CustomElementbuilders now acceptChildAttributealongside ordinary attributes, so published Submodel attribute groups compose with them just like native HTML element builders. Thank you to @jay-zahiri for noting this issue!Scene preserves every OutMessage emitted by one update-producing step in runtime order. Use
expectOutMessageswhen a batch produces several.The renderer does less work in its hot path by caching unchanged document metadata, writing ordinary properties directly, and skipping module scans when a VNode has no module data.
Dialog now releases its resources when
ShowDialogfails or is interrupted, and whenCloseDialogfinds that the element is already gone. Thank you to @wmaurer for the fixes!ManagedResource now clears its reference and dispatches its release Message when the user-provided release effect fails.
The deprecated
h.OnClickFocusis gone. Useh.OnClick(message, { focusSelector }).
The packages now build and test with TypeScript 7 as well. The full 0.154.0 and 0.155.0 release notes cover every package and migration detail.
Thanks to everyone building with Foldkit!
Devin