Serializable State
Marko seamlessly picks up where the server left off when it comes to events, scripts and client side updates through state. In order to do this Marko will attempt to serialize as little data as possible from the server to the client.
Most standard data types can be serialized, including:
- Primitives:
null,boolean,number,string,bigint - Arrays and plain objects with serializable values
- Dates
- Map, Set
- Typed arrays and ArrayBuffer/DataView
- URL and URLSearchParams
- Additional built-in JS and Browser objects
- For a complete list, see the serializer file from source
... and many more.
Reaching the Client
State does not travel through a separate data request or a client-side re-render. When a template renders on the server, inline <script> tags are emitted alongside the markup, carrying the serialized state and markers that tie each value back to its place in the DOM. When the page loads, the runtime reads this data and resumes exactly where the server finished, without re-executing the template.
The wire format of this data is an implementation detail that changes between versions, so application code should never read or write it directly.
Since values are only serialized when client-side logic can reference them, state that is rendered into HTML and never touched again in the browser adds nothing to the serialized client-state payload. Fine-Grained Bundling explains how the compiler makes that determination.
Unserializable Data
Some values cannot be serialized. When these values are encountered the Marko runtime will provide a helpful message to locate the relevant code.
Examples of unserializable data include:
- Closures (top level functions are fine!)
- Functions that come from arbitrary javascript code or imports
- Class instances (except built-ins explicitly supported by the runtime)
- DOM nodes and elements
Most functions and closures are serializable.
<let/handler=null>
<const/onSecondClick() {
; // serializable!
}>
<button onClick() {
handler?.();
handler = onSecondClick;
}/><let/handler=null>
<const/onSecondClick() {
// serializable!
}>
<button onClick() { handler?.(); handler = onSecondClick }/>let/handler=null
const/onSecondClick() {
; // serializable!
}
button onClick() {
handler?.();
handler = onSecondClick;
}let/handler=null
const/onSecondClick() {
// serializable!
}
button onClick() { handler?.(); handler = onSecondClick }<!-- ❌ BAD: custom class instance in state-->
<let/state=(new Cart())>
<!-- ❌ BAD: DOM nodes in state-->
<let/state={
el: document.body
}>// ❌ BAD: custom class instance in state
<let/state=new Cart()>
// ❌ BAD: DOM nodes in state
<let/state={ el: document.body }><!-- ❌ BAD: custom class instance in state-->
let/state=(new Cart())
<!-- ❌ BAD: DOM nodes in state-->
let/state={
el: document.body
}// ❌ BAD: custom class instance in state
let/state=new Cart()
// ❌ BAD: DOM nodes in state
let/state={ el: document.body }Further Reading
Contributors
Helpful? You can thank these awesome people! You can also edit this doc if you see any issues or want to improve it.