View functions that run in the browser can also run on a vanilla GHC server to produce static HTML. The client then “hydrates” that HTML — populating the virtual DOM from the real DOM instead of redrawing it — which avoids a blank-page flash on load and improves SEO.
The SSR cabal flag
Enable server-side rendering by building with theSSR cabal flag:
textcallshtmlEncodebefore storing the string, ensuring user content is always escaped when serialized to HTML.getInitialComponentModelinMiso.Html.RenderuseshydrateModelto load server-injected state.renderBuilderevaluates childVCompviews using the hydrated model rather than the default model.
The
SSR flag is only relevant for the server executable. Your client (WASM or JS) binary is built without it.Rendering HTML on the server
ImportMiso.Html.Render and use the ToHtml typeclass:
View m a and [View m a]:
- Self-closing tags for
area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbrand their SVG/MathML equivalents. - Boolean HTML attributes (
disabled,checked,required, etc.) — present whenTrue, omitted whenFalse. - Inline
Stylesasstyle="k:v;". ClassListasclass="a b c".Onevent handlers are silently dropped (they have no HTML representation).- A special
VNode _ "doctype" [] []renders as<!doctype html>. - Adjacent
VTextsiblings are collapsed into one text node, matching the browser’s parsing behavior and preventing hydration mismatches.
text vs textRaw and HTML escaping
text for user-supplied strings. Use textRaw only when you are certain the string is already safe HTML (for example, pre-processed markdown output).
The htmlEncode helper is available for manual use:
Starting the client with hydration
Usemiso (instead of startApp) on the client when the page has been pre-rendered by the server:
<body> first).
Loading server-injected state with hydrateModel
The Component type exposes a hydrateModel field for loading state that was injected into the page by the server:
<script> tag on the server and read it back in hydrateModel:
Debugging hydration mismatches with LogLevel
When the server-rendered DOM and the virtual DOM produced by view differ, miso can warn you in the browser console:
DebugHydrate, the runtime compares each virtual node against the corresponding real DOM node and logs any discrepancy. This makes it straightforward to identify which part of the view function produces output that does not match the server HTML.
Recommended project layout
A standard miso SSR project uses two executables in one cabal file that share a common library:Common module imports from miso and defines the View, Model, Action, and Router types. Both executables import Common. The CPP flag -DSSR switches the text and renderBuilder behavior in the server build.
The prerender function
prerender is a convenience wrapper around miso for applications that use SSR but do not need URL-based routing:
prerender when the server always renders the same page regardless of URL and you just want hydration without a routerSub: