miso
A tasty Haskell front-end web framework
| LTS Haskell 24.57: | 1.8.7.0 |
| Stackage Nightly 2026-08-31: | 1.13.0.0 |
| Latest on Hackage: | 1.13.0.0 |
miso-1.13.0.0@sha256:6977b22306367b628fa3b5eff7ad6df033538e5dfa66335f1716ee5d99ba1537,8571Module documentation for 1.13.0.0
- Miso
- Miso.CSS
- Miso.Canvas
- Miso.Concurrent
- Miso.Cookie
- Miso.DSL
- Miso.Data
- Miso.Date
- Miso.Effect
- Miso.Event
- Miso.EventSource
- Miso.FFI
- Miso.Fetch
- Miso.Html
- Miso.JSON
- Miso.Lens
- Miso.Mathml
- Miso.Media
- Miso.Navigator
- Miso.Prelude
- Miso.Property
- Miso.PubSub
- Miso.Random
- Miso.Reload
- Miso.Router
- Miso.Runtime
- Miso.State
- Miso.Storage
- Miso.String
- Miso.Subscription
- Miso.Svg
- Miso.Trace
- Miso.Types
- Miso.Util
- Miso.WebSocket
Key features
- Virtual DOM with recursive diffing and patching algorithm
- Attribute and property normalization, event delegation, and event batching
- Model-View-Update paradigm
- Pure by default
- SVG, 2D Canvas, and WebGL (via three.js)
- Fetch, Server-Sent Events, and WebSocket support
- Type-safe client-side routing
- An extensible subscription system for long-running effects and third-party library integration
- Lifecycle hooks (
onCreated,onDestroyed,mount,unmount) - Component, Context, Fragment and Props features.
It makes heavy use of the GHC JavaScript FFI and maintains minimal dependencies. It can be considered a shallow embedded domain-specific language for modern web programming. Compilation targets include JavaScript and WebAssembly via GHC. Hot reload is provided through WASM browser mode integrated with ghciwatch.
[!TIP] See the miso organization on GitHub for the full ecosystem of packages and examples 🍜
Table of Contents
- Playground
- Quick Start (Nix)
- Manual Setup (GHCup / Cabal)
- Hot Reload
- Installation
- Haddocks
- Wiki
- Architecture
- Examples
- HTTP
- Testing
- Native
- Benchmarks
- Nix
- Community
- Maintainers
- Commercial
- Contributing
- Contributors
- Partnerships
- Backers
- Organizations
- History
- License
Playground 🛝
An interactive playground is available at try.haskell-miso.org. It allows editing and running applications directly in the browser without any local toolchain setup, and is useful for experimentation and sharing minimal reproducible examples.
Quick Start (Nix) ⚡
[!TIP] The miso-sampler template repository includes a counter application with build scripts for WebAssembly, JavaScript, and native GHC targets.
The following requires Nix Flakes. See also Binary cache to avoid rebuilding dependencies.
# Install nix
curl -L https://nixos.org/nix/install | sh
# Enable flakes
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
# Clone, build and serve
git clone https://github.com/haskell-miso/miso-sampler && cd miso-sampler
nix develop .#wasm --command bash -c 'make && make serve'
Manual Setup (GHCup / Cabal)
To develop applications without Nix, acquire GHC and cabal via GHCup.
[!TIP] For users new to Haskell tooling, GHCup is the recommended way to install both GHC and cabal.
A minimal application requires three files:
cabal.projectapp.cabalMain.hs
cabal.project
packages:
.
source-repository-package
type: git
location: https://github.com/dmjio/miso
branch: master
[!NOTE] Pinning to a specific
tag:orcommit:rather thanbranch: masteris recommended for reproducible builds.
app.cabal
Using cabal-version: 2.2 or later enables common stanzas, which allow a single .cabal file to target both the WASM and JS backends.
cabal-version: 2.2
name: app
version: 0.1.0.0
synopsis: Sample miso app
category: Web
common options
if arch(wasm32)
ghc-options:
-no-hs-main
-optl-mexec-model=reactor
"-optl-Wl,--export=hs_start"
cpp-options:
-DWASM
if arch(javascript)
ld-options:
-sEXPORTED_RUNTIME_METHODS=HEAP8
executable app
import:
options
main-is:
Main.hs
build-depends:
base, miso
default-language:
Haskell2010
Main.hs
A counter application demonstrating the Model-View-Update pattern:
----------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE CPP #-}
----------------------------------------------------------------------------
module Main where
----------------------------------------------------------------------------
import Miso
import qualified Miso.Html as H
import Miso.Lens
----------------------------------------------------------------------------
-- | Sum type for App events
data Action
= AddOne
| SubtractOne
| SayHelloWorld
deriving (Show, Eq)
----------------------------------------------------------------------------
-- | Entry point for a miso application
main :: IO ()
main = startApp defaultEvents app
----------------------------------------------------------------------------
-- | WASM export, required when compiling w/ the WASM backend.
#ifdef WASM
foreign export javascript "hs_start" main :: IO ()
#endif
----------------------------------------------------------------------------
-- | `vcomp` takes as arguments the initial model, update function, view function
app :: App Int Action
app = vcomp 0 updateModel viewModel
----------------------------------------------------------------------------
-- | Updates model, optionally introduces side effects
updateModel :: Action -> Effect context props Int Action
updateModel = \case
AddOne -> this += 1
SubtractOne -> this -= 1
SayHelloWorld -> io_ $ do
alert "Hello World"
consoleLog "Hello World"
----------------------------------------------------------------------------
-- | Constructs a virtual DOM from a model
viewModel :: context -> props -> Int -> View context Action
viewModel _context _props x = vfrag
[ H.button_ [ H.onClick AddOne ] [ text "+" ]
, text (ms x)
, H.button_ [ H.onClick SubtractOne ] [ text "-" ]
, H.br_ []
, H.button_ [ H.onClick SayHelloWorld ] [ text "Alert Hello World!" ]
]
----------------------------------------------------------------------------
Hot Reload 🔥
Hot reload is supported via WASM browser mode and ghciwatch. This provides incremental recompilation with automatic browser refresh on file changes. See the miso-sampler browser mode documentation for setup instructions.
Installation
See Installation for platform-specific installation instructions.
Haddocks
Official API reference. See also the Miso module for a guided entry point into the library.
| Platform | URL |
|---|---|
| GHCJS | Link |
| GHC | Link |
Wiki
See the DeepWiki entry for an AI-assisted exploration of the source code.
Architecture
miso follows the Model-View-Update (MVU) pattern. A Component is parameterized by a model type and an action type. The update function maps actions to Effect values — a monad over the Reader/Writer/State stack — which can both modify the model and schedule IO operations. Long-running effects are expressed as Subscriptions that push actions into the component via a Sink.
For (client/server) applications, the recommended layout is a single .cabal file with separate executable stanzas conditioned on the compiler target. An example of this structure is the haskell-miso.org source.
[!TIP] For a worked example of a Nix-based client/server deployment, see the nix scripts for haskell-miso.org.
Examples
Examples are hosted under the haskell-miso GitHub organization. Each repository contains its own build instructions. The recommended approach is to build via nix.
[!TIP] Use cachix to avoid rebuilding shared dependencies:
cachix use haskell-miso-cachix
| Name | Description | Source | Demo | Author |
|---|---|---|---|---|
| TodoMVC | TodoMVC reference implementation | Source | Demo | @dmjio |
| 2048 | Clone of the 2048 sliding-tile game | Source | Demo | @ptigwe |
| Flatris | Tetris variant | Source | Demo | @ptigwe |
| Plane | Flappy-bird-style game | Source | Demo | @Lermex |
| Snake | Classic Snake game | Source | Demo | @lbonn |
| SVG | SVG rendering | Source | Demo | @dmjio |
| Fetch | HTTP API interaction via Fetch | Source | Demo | @dmjio |
| File Reader | FileReader API | Source | Demo | @dmjio |
| Mario | Physics-based platformer | Source | Demo | @dmjio |
| WebSocket | WebSocket communication | Source | Demo | @dmjio |
| Router | Client-side routing | Source | Demo | @dmjio |
| Canvas 2D | 2D Canvas rendering | Source | Demo | @dmjio |
| MathML | MathML rendering | Source | Demo | @dmjio |
| Simple | Counter (minimal example) | Source | Demo | @dmjio |
| SSE | Server-Sent Events | Source | Demo | @dmjio |
| Three.js | 3D rendering via Three.js | Source | Demo | @juliendehos |
| Space Invaders | Space Invaders clone | Source | Demo | @juliendehos |
| Audio | Audio playback | Source | Demo | @juliendehos |
| Video | Video playback | Source | Demo | @juliendehos |
| WebVR | WebVR via A-Frame | Source | Demo | @dmjio |
| Reactivity | Fine-grained reactive updates | Source | Demo | @dmjio |
| Chess | Chess game | Source | Demo | @dmjio |
Interacting with HTTP APIs 🔌
Two approaches are supported:
-
For simple JSON-based APIs, use the Fetch module directly.
-
For more complex cases, define a Servant API and derive client functions via servant-miso-client.
The Fetch example (Demo) demonstrates the required setup. Add the following to
cabal.projectto useservant-miso-client:source-repository-package type: git location: https://github.com/haskell-miso/servant-miso-client tag: master
Testing âś…
The test suite spans three layers:
- Unit tests — the TypeScript runtime (virtual DOM, diffing, event delegation) is tested with bun, covering the core
diffengine and supporting utilities. - Integration tests — Haskell internals are exercised via a WASM test suite that runs the runtime in a headless browser environment, verifying component lifecycle, subscriptions, and state transitions.
- End-to-end tests — selected applications such as TodoMVC are tested end-to-end against a live browser to validate full-stack rendering and event handling.
A full coverage report for the TypeScript layer is available at coverage.haskell-miso.org.
[!NOTE] To run the TypeScript tests, install bun first.
$ curl -fsSL https://bun.sh/install | bash
or
$ nix-env -iA bun -f '<nixpkgs>'
and
$ bun install && bun run test
Native 📱
iOS and Android applications are supported via LynxJS. See the miso-lynx repository for details.
Benchmarks 🏎️
According to benchmarks, miso performs competitively relative to other frameworks.
Nix
Nix provides a reproducible environment for building, configuring, and deploying applications. The haskell-miso.org source serves as a reference for this workflow.
Pinning nixpkgs 📌
By default, miso uses a pinned version of nixpkgs known as pkgs.
[!NOTE]
misoalso maintains alegacyPkgsnixpkgs pin for tools such asnixopsand for builds using the originalGHCJS 8.6backend.
Binary cache
Linux and macOS users can use a binary cache to avoid rebuilding dependencies. Follow the setup instructions on cachix.
$ cachix use haskell-miso-cachix
For CI pipelines using GitHub Actions:
- name: Install cachix
uses: cachix/cachix-action@v16
with:
name: haskell-miso-cachix
Community :octocat:
Maintainers
Commercial 🚀
Since its launch, miso has been deployed across a range of domains, including quantitative finance, network security, defense research, academia, SaaS, the public sector, and non-profit organizations. The largest known deployment consisted of approximately 200,000 LOC serving over 10,000 users.
Contributing
Contributions are welcome. Open an issue or submit a pull request.
See CONTRIBUTING for guidelines.
Contributors 🦾
[!NOTE] This project exists thanks to all the people who contribute.
Partnerships 🤝
For inquiries regarding feature sponsorship or corporate partnerships, contact [email protected].
Backers
Become a financial contributor to help sustain the project.
organizations
Support this project with your organization. Your logo will appear here with a link to your website.
History 📜
miso is a portmanteau of micro and isomorphic.
miso was initiated in 2016 as a research project exploring two directions:
- Expressing the Elm architecture in GHCJS as an embedded domain-specific language
- Implementing reconciliation and isomorphic rendering techniques from the JavaScript ecosystem, within a purely functional setting.
The project addresses the JavaScript problem in Haskell by providing component abstractions and rendering primitives familiar to practitioners of frameworks such as React and Vue.js. The library has since expanded to include multiple rendering backends and native mobile support for iOS, Android, and HarmonyOS via LynxJS.
License
BSD3 © dmjio
Changes
Changelog
All notable changes to miso are documented here.
1.13.0.0
Added
-
Native mobile backend.
misocan now target native mobile devices by driving the Lynx dual-thread runtime instead of the browser DOM. NewMiso.Nativeentry point (native/nativeWithContext), theMiso.Native.Element.*element / event / property / method vocabulary, and main-thread event handlers for low-latency gestures. Gated behind thenativecabal flag (-fnative); web / WASM builds are unaffected.Because the flag is off by default, the
Miso.Native.*modules do not appear in the Hackage-generated documentation — build locally with-fnative, or see thesample-app-nativedirectory for a worked example with iOS and Android hosts. -
App-global
context. A single value shared by everyComponentin the tree (miso’s analogue of React Context): seed withstartAppWithContext, read withgetContext(or the first argument toview), update withmodifyContext/modifyContext_/putContext, and opt components into context-driven re-renders withuseContext.ComponentInfogained acomponentInfoContextlens. The motivating use case is propagating settings such as locale or theme to every component without threading them throughprops. -
Cookie Store API. New
Miso.Cookiemodule wrapping the browser’s CookieStore API asEffectcombinators —cookieGet,cookieGetAll,cookieSet,cookieDelete,cookieDeleteWith, theCookierecord anddefaultCookieconstructor, plus_-suffixed synchronous variants.Miso.Subscription.CookieaddscookieChangeSubfor subscribing toCookieChangeEvents. Requires a secure context (HTTPS orlocalhost); on browsers without the API (e.g. Firefox) the error callback fires andcookieChangeSubis a no-op. -
canvasSub. NewMiso.Subscription.Canvasmodule.canvasSubdrives a<canvas>in a tightrequestAnimationFrameloop, bypassing virtual DOM construction entirely — unlikeMiso.Canvas, whosedrawruns during the diffing process on discrete events. Pair it withonCreatedWith/onDestroyedandstartSub/stopSubto start the loop when the canvas mounts and stop it on unmount. The draw callback receives each frame’s high-resolution timestamp and a snapshot of the component’s current model (see theSubchange below), and the queued frame is cancelled before the callback is freed on teardown. -
Miso.Trace. A browser-console analogue ofDebug.Tracefor debugging pure code such asviewfunctions or helpers called fromupdate.trace,traceId,traceWith,traceShow,traceShowId,traceShowWith,traceMandtraceShowMlog withconsole.log; thetraceWarn*andtraceError*families log withconsole.warnandconsole.errorrespectively, gaining the browser’s severity filtering and stack traces.traceTogeneralises over anyMisoString -> IO ()console function fromMiso.FFI. LikeDebug.Trace, these are built onunsafePerformIOand are a debugging aid only. -
Synchronous
Miso.Fetchvariants._-suffixed counterparts for the whole surface —getJSON_,postJSON_,postJSON'_,putJSON_,getText_,postText_,putText_,getBlob_,postBlob_,putBlob_,getFormData_,postFormData_,putFormData_,getUint8Array_,postUint8Array_,putUint8Array_,getArrayBuffer_,postArrayBuffer_,putArrayBuffer_,postImage_,putImage_. Each blocks the calling thread and returnsEither (Response error) (Response body). Best used insideMiso.Effect.io/io_so the scheduler thread is not blocked. -
Cross-thread effects.
runOnBGandrunOnMain(with the supportingThreadtype) dispatch an action’supdateonto the background (BTS) or main (MTS) thread of the Lynx runtime. Off the native runtime, or when already on the target thread, both behave as an ordinaryissue. -
Main-thread event handlers.
onMain/onMainWithOptionsinMiso.Eventregister handlers that run directly on the main thread, for low-latency gesture and animation work.Miso.Native.MainThreadprovidesMainThreadRefand the imperative operations those handlers drive.eventHandlerConvert/eventHandlerDecoderand theEventHandlertype are exported for building custom handlers. -
Static components.
mountStaticandmountStaticWithPropsmount aComponentthrough aStaticPtr(SomeStaticComponent), so the component survives the dual-thread boundary;vcomp_/vcompturn the resulting pointer into aView. Unlike the non-static combinators these need no key — the compile-timeStaticKeysupplies identity. To opt a statically mounted child intocontextre-renders, set the field directly:mountStatic comp { useContext = True }.mountUseContextis the non-static equivalent. -
Every exported name is documented.
cabal haddockreported 118 undocumented exports across 34 modules — mostly the Lynx event payloads, decoders, method parameter records andEventsmaps underMiso.Native.Element.*. All now carry Haddock. -
Context-seeding SSR entry points.
misoWithContextandprerenderWithContexthydrate a server-rendered page with an explicit initialcontext;setContextseeds the global context for use from theToHtmlrenderer.Miso.Reloadgained matchingliveWithContextandreloadWithContext. -
Lynx thread detection.
getThreads,onBTSandonMTSinMiso.FFIreport which thread the current code is executing on. -
CSS helpers.
transition_builds a single shorthandtransitiondeclaration (so an imperativetransition: nonereset on the main thread clears it as one key), andcubicBezierproduces acubic-bezier(…)timing function. -
Miso.DSLadditions.awaitfor awaiting a JS promise from Haskell, and theJSExceptiontype (which now has anExceptioninstance). -
DirectEvents.VNodecarries a set of directly-dispatched events, readable vianodeDirectEvents, used by the native runtime to skip the scratch-node round trip. -
Types that were reachable but not exported. Several types appeared in exported signatures without being exported themselves, so callers could not name them:
Consumed(the payload ofMiso.Native.Element.List.Method’s callback),GetTextBoundingRect(the parameter ofgetTextBoundingRect),ListItemInfo,AnimationTypeandUIAppearanceDetailEventType(field types of exported Lynx event records), andComponentIds(the type ofComponentState’s_componentChildren).Miso.JSONnow exportsToJSONwith both methods —toJSONListwas hidden, so it could not be overridden outside the module — along with the four generic-deriving classes missing from itsGenericsgroup (GToJSONRep,GToJSONSumNullary,GFromJSONRep,GFromJSONSumNullary).Miso.Lens.Genericlikewise exports the type-level machinery itsHasLensinstances mention (GSet,GetFieldType,TotalityCheck,And,Or). -
aesoncabal flag. When enabled (-faeson, off by default),Miso.JSONkeeps its API but is defined in terms of aeson:Value,Object, andParserbecome aeson’s types, so existing aesonToJSON/FromJSONinstances work directly withMiso.Fetch,Miso.WebSocket, and the event decoders. Signatures are unchanged — the accessors still takeMisoStringkeys,withArraystill passes the continuation a[Value],withNumberstill passes aDouble, andResultstill carriesMisoStringerror messages. On the JS / WASM backends orphan instances makeJSStringa first-class JSON citizen. Miso’s own generic-deriving machinery (GToJSONet al.) is not exported in this mode; aeson’sgenericToJSON/Options/camelTo2are re-exported instead. CI runs the WASM integration suite in both modes. -
textcabal flag on WASM. When enabled (-ftext, off by default),MisoStringisData.Text.Textinstead ofJSStringon the WASM backend too (previously this was only possible on theVANILLA/ SSR build).Data.JSStringremains the FFI boundary type, so DOM writes still convertText -> JSStringon the way out. Number formatting and parsing take advantage of this to avoid unnecessary FFI round trips:toMisoStringonInt/Word/Double/FloatbuildsTextdirectly viaData.Text.Lazy.Builder(decimal/realFloat) instead of allocating a throwawayJSValvia JS’s.toString(), since GHC’sShowformatting is what these functions target on this backend regardless. Likewise,fromMisoStringonInt/Word/Double/Floatparses directly withData.Text.Readinstead of round-tripping throughJSString/parseInt/parseFloat, while reproducing the JS parsers’ semantics: leading/trailing whitespace and trailing garbage are ignored, a leading+/-is accepted, and integers with a0x/0Xprefix parse as hexadecimal. CI gained aplaywright-wasm-aeson-texttarget that runs the WASM integration suite with both theaesonandtextflags enabled together.
Changed
-
Breaking:
ViewandAttributegained type parameters.View context model actionandAttribute model action. This lets event handlers read the currentmodeland supports the native dual-threadstatichandler protocol.VNodenow carries aDirectEventsset, the key moved intoSomeComponent (Maybe Key) …, andVComp/VCompStatic/SomeStaticComponentwere restructured. Downstreamviewand attribute signatures must be updated accordingly. -
Breaking:
Subgained amodeltype parameter.Sub actionis nowSub model action, and a subscription receives a second argument — anIO modelthat returns a snapshot of the component’s current model:type Sub model action = Sink action -> IO model -> IO (). This lets long-running subscriptions (likecanvasSub) read the latest model without threading it through actions. All bundled subscriptions were updated; user-defined subscriptions that ignore the model need only accept (and discard) the extra argument, e.g.tickSub sink _ = forever (threadDelay delay >> sink Tick).mapSub,createSub, andstartSubwere updated accordingly. -
Breaking:
Miso.Bindingwas removed. The experimental lens-based parent/child model synchronisation mechanism (Binding,Bindings,Precedence, and thebindingsfield onComponent) is gone, along with its propagation phase in the scheduler. Use the new app-globalcontextfor shared state, or asynchronous messaging viabroadcast/Miso.PubSubfor point-to-point communication. -
Breaking:
parentandROOTwere removed.Componentno longer carries aparent; theROOTmarker that demarcated the top of the page is unnecessary without it. Both are superseded bycontext. -
Breaking:
Miso.Types.keyedwas removed. Use the keyed constructors directly:textKey/textKey_for text,fragment_/vfrag_for fragments,mount_/vcomp_/mountStaticfor components, andkey_in the attribute list for element nodes. -
Breaking: runtime internals dropped from
Miso.FFI.mountComponent,unmountComponentandmodelHydration(andgetComponentContextfromMiso.FFI.Internal) were documented as runtime-use-only and have been removed as part of the dual-thread rework. They have no user-facing replacement. -
Breaking:
autocomplete_takes aMisoString. It wasBool -> Attribute action, which could only produce"on"/"off"and could not express the many other valid values ("email","new-password", …). It is nowMisoString -> Attribute action; replaceautocomplete_ Truewithautocomplete_ "on". -
Breaking:
Miso.Util.Parser.endOfInputwas generalised fromParser a ()toParserT r [a] [] (). Call sites are unaffected unless they carried an explicit type annotation. -
MisoStringlengthandtakeare code-point based on WASM. They previously counted UTF-16 code units, so a string holding a single astral-plane character (an emoji, say) reported a length of 2. They now agree withData.Textand with the GHCJS backend. Only the WASM backend was affected. -
contextno longer requiresToJSON/FromJSON. The constraints were unused —contextis never sent across the dual-thread boundary.
Removed
Miso.String.QQ. ThemisoStringQuasiQuoter for multilineMisoStringliterals is gone. GHC’sMultilineStringsextension (GHC 9.12+) covers the use case directly — enable the pragma and write triple-quotedMisoStringliterals. (Miso.FFI.QQandMiso.Lens.TH, the othertemplate-haskell-flag modules, are unaffected.)
Fixed
-
Miso.Fetch’snoneresponse type no longer double-fires the success callback.fetchCorecalled the success callback directly forresponseType == "none"and then fell through into a second, unconditional.thenthat called it again withbody: undefined. Everypost*/put*variant that discards the response body (postJSON,postJSON_,putText,putBlob_, etc.) dispatched its success action twice per request. -
Native: attribute removal actually removes the attribute. The MTS drawing context’s
removeAttributecalled__SetAttribute(node, key, ''). The engine’sElement::SetAttribute(lynx/core/renderer/dom/element.cc) only takes the removal branch when the value is lepus-empty (null/undefined) — an empty string is an ordinary string value, so it was stored inupdated_attr_map_instead of being removed. Every prop diffed off a native element (dom.ts’sdiffProps, which routes native removals through this path) was setting it to''rather than clearing it. Now passesnull. -
rAFSubnow cancels the pending animation frame on unsubscribe. Release freed therequestAnimationFramecallback without cancelling the frame already queued in the browser; the next frame then invoked a freed callback and crashed the WASM RTS withinternal error: stg_ap_p_ret.Miso.Canvas’sdrawalso moved from asyncCallbackto anasyncCallback, fixing aschedule: re-entered unsafelycrash when a component unmounted mid-diff. -
Non-bubbling media events are registered in the capture phase.
durationchange,loadeddata,loadedmetadataandloadstartdo not bubble, so their delegated listeners — registered in the bubble phase — never received them andonLoadedMetadataand friends silently never fired. They are now registered with capture, like the other non-bubbling entries inmediaEvents. -
Native: the layout custom event is recognised under its released name. Released Lynx engines (e.g. LynxExplorer apps) emit it as
layout, while newer Lynx sources emitlayoutchange; miso only listened for the latter, soonLayoutChangenever fired on released engines.onLayout/onLayoutMainWithare added as aliases so apps can bind both when the host engine version is unknown. -
Native:
consumeSlideEvent_sends the shape Lynx expects. Lynx parsesconsume-slide-eventas[start, end]angle-range pairs (degrees, -180..180), but the binding serialised a flat list of angles instead of paired ranges, a shape the engine silently ignores. -
autocorrect_andspellcheck_wrote to the wrong attribute. Both emittedautocompleteinstead of their own attribute name.spellcheck_additionally now emits"true"/"false"rather than"on"/"off". -
MOUNTerrors on a missingdomRefinstead of synthesizing a bogus parent node and failing later in the diff. -
Key-based model recovery is gated on
liveMode, so a component no longer reuses an unrelated model outside of hot reload. -
pendingStaticKey/pendingMainThreadare reset before plainOnhandlers run, preventing state from one handler leaking into the next. -
-fssrcompiles together with-fnative. -
JSExceptionderivesException, so it can bethrown andcatched normally. -
Non-bubbling
mouseleave/pointerleaveare registered in the capture phase. Neither event bubbles per the DOM spec (unlikemouseout/pointerout, which correctly bubble), but the delegated listener was registered in the bubble phase, soonMouseLeave/onPointerLeavehandlers on any non-root element silently never fired. Same bug class as the non-bubbling media events fix above, extended to these two. -
vcompwas misused as a synonym forcomponentinMiso.hs’s documentation.vcompbuilds aVCompStaticfrom aStaticPtr(the static-component feature), not aComponentfrommodel/update/viewfunctions. The module’s own “Your first Component” example and two other doc snippets usedvcompwherecomponentwas meant, so copying them verbatim would not typecheck. -
MisoString’sdropis code-point-based on WASM, matchingtake/length.take/lengthwere made code-point-based to fix astral-character (e.g. emoji) miscounting, butdropwas left on raw UTF-16 slicing. SincesplitAtis defined as(take n xs, drop n xs), the two disagreed on where positionnfalls for any string containing an astral character before it, corrupting the split. -
-ftextparseIntmis-parses negative hex."-0x1A"checked for a0x/0Xprefix before stripping a sign, so it never matched and fell through to a decimal parse of"0x1A", silently returning0instead of-26. The sign is now stripped first, then the remainder is checked for a hex prefix. -
eventJSONdecodes a null/undefined path asnullinstead of crashing. A decoder path landing onnull/undefined—relatedTarget,currentTarget,form,list, etc. are all legitimately null/undefined on many real DOM events — hit'length' in objon the nullish value and threwTypeError, crashing event dispatch instead of decoding the field asnull. An intermediate nullish step one segment earlier had the same problem; both are now handled. -
freeLifecycleHooksfrees a component’smount/unmountcallbacks again. It read themount/unmountfields off the component’s own rendered content root instead of theVCompwrapper node that actually holds them (reachable one hop up, via the content root’sparentlink), sofromJSValalways failed andfreeFunctionwas never called. Every non-rootComponentunmount — normal teardown and every GHCi hot-reload cycle — leaked the closuresmountCallback/unmountCallbackcapture, which includes the wholeinitializeclosure (app,events,sink,model). See Note [Freeing event handler callbacks] inMiso.Runtime.
Performance
-
Short-lived
JSValhandles are freed eagerly in the WASM runtime. On the WASM backend everyJSValcarries a weak pointer with a C finalizer, and the RTS copies all of them at every GC — so the hundreds of scratch handlesbuildVTreeallocates per frame made GC pauses scale with handle churn (~100 ms pauses with ~50 KB of live data in profiling). The runtime now releases handles nothing else can reach via the newMiso.DSL.freeJSVal(GHC.Wasm.Prim.freeJSValon WASM, a no-op on other backends), and event handler callbacks are freed when their vtree is replaced. Measured on miso-mario,C_FINALIZER_LISTcopied per GC dropped from 7.3 MB to 2.3 MB. See Note [Freeing VTree handles] inMiso.Runtime. -
StableNamedirty-checking extended tocontextandprops.modelCheckwas generalised todirtyCheck :: Eq a => a -> a -> Booland applied to the remaining sites that performed a full structuralEqwalk on every check. The common case — two reads of the sameIORefreturning the same heap object — now short-circuits on pointer equality, which matters most for large contexts such as i18n translation maps. -
Main-thread events dispatch directly, with no scratch-node or JS round trip.
-
The thread environment (
mts/bts/web) is cached as a static global in the runtime rather than re-queried on everyinitialize/initComponent.