Overview // Marko WidgetsImprove this page
Marko Widgets extends the Marko templating engine to provide a simple and efficient mechanism for binding behavior to UI components rendered on either the server or in the browser. In addition, changing a widgets state or properties will result in the DOM automatically being updated without writing extra code. Marko Widgets has adopted many of the good design principles promoted by the React team, but aims to be much lighter and often faster (especially on the server). When updating the view for a widget, Marko Widgets uses DOM diffing to make the minimum number of changes to the DOM through the use of the morphdom module.
Features
- Simple
- Clean JavaScript syntax for defining widgets
- Utilizes Marko templates (an HTML-based templating language) for the view
- Supports stateful and stateless widgets
- No complex class hierarchy
- Simple, declarative event binding for both native DOM events and custom events
- Lifecycle management for widgets (easily destroy and create widgets)
- Events bubble up and view state changes trickle down
- Only need to understand a few concepts to get started
- High performance
- Lightning fast performance on the server and in the browser (see Marko vs React: Performance Benchmark)
- Supports streaming and asynchronous rendering
- Efficient binding of behavior of UI components rendered on the server and in the browser
- Efficient updating of the DOM via the following tricks:
- DOM diffing is used to make the minimum number of changes to the DOM using the morphdom module.
- Batched updates
- When re-rendering a widget, nested widgets are reused
- Only widgets whose state changed are re-rendered
- Full re-rendering of a widget can be short circuited if state transition handlers are provided
- For container components, nested body DOM nodes are automatically preserved
- Entire DOM subtrees can be preserved between rendering
- Smart template compilers to offload as much work to compile time
- Very efficient event delegation
- Fast serialization of state from the server to the browser using warp10
- Lightweight
- Extremely small JavaScript runtime (~6.3 KB gzipped)
- No dependencies on any other JavaScript library such as jQuery
- Focused exclusively on the UI view (easily mix and match with other libraries/frameworks)
Design Philosophy
- A UI component should encapsulate view, behavior and styling
- A complex page should be decomposed into modular UI components
- UI components should be used as building blocks
- A component's view should be driven by a pure function that accepts an input state and produces output HTML
- A UI component should be independently testable
- A UI component should not leak its internal implementation
- A UI component should be installable via npm
- A UI component should play nice with other frameworks and libraries
- UI components should be easily composable
- Developers should not need to manually manipulate the DOM
Sample Code
Marko Widgets allows you to declaratively bind behavior to an HTML element inside a Marko template. The widget provides the client-side behavior for your UI component.
Stateless Widget
src/components/app-hello/template.marko
<div w-bind>Hello !</div>
src/components/app-hello/index.js
moduleexports =;
Congratulations, you just built a reusable UI component! Your UI component can be embedded in other Marko template files:
<div><app-hello name="Frank"/></div>
In addition, your UI can be rendered and added to the DOM using the JavaScript API:
var widget =;// Changing the props will trigger the widget to re-render// with the new props and for the DOM to be updated:widget;
Stateless Widget with Behavior
src/components/app-hello/template.marko
<div w-bindw-onClick="handleClick">Hello !</div>
src/components/app-hello/index.js
moduleexports =;
Stateful Widget
Let's create a stateful widget that changes to yellow when you click on it:
src/components/app-hello/template.marko
<div w-bindw-onClick="handleClick"style="background-color: ">Hello !</div>
src/components/app-hello/index.js
moduleexports =;
Stateful Widget with Update Handlers
If you want to avoid re-rendering a widget for a particular state property change then simply provide your own method to handle the state change as shown below:
src/components/app-hello/template.marko
<div w-bindw-onClick="handleClick"style="background-color: ">Hello !</div>
src/components/app-hello/index.js
moduleexports =;
Complex Widget
<div w-bind><app-overlay title="My Overlay"w-id="overlay"w-onBeforeHide="handleOverlayBeforeHide">Body content for overlay.</app-overlay><button type="button"w-onClick="handleShowButtonClick">Show Overlay</button><button type="button"w-onClick="handleHideButtonClick">Hide Overlay</button></div>
Below is the content of index.js
where the widget type is defined:
moduleexports =
Container Widget
A container widget supports nested content. When the container widget is re-rendered, the nested content is automatically preserved.
src/components/app-alert/template.marko
<div class="alert alert-" w-bind><i class="alert-icon"/><span w-body></span></div>
src/components/app-alert/index.js
moduleexports =
The widget can then be used as shown below:
<app-alert message="This is a success alert"/><app-alert>This is a success alert</app-alert><app-alert message="This is a failure alert" type="failure"/><app-alert type="failure">This is a failure alert</app-alert>
Preserving DOM Nodes during Re-render
Sometimes it is important to not re-render a DOM subtree. This may due to either of the following reasons:
- Improved performance
- DOM nodes contains externally provided content
- DOM nodes have internal state that needs to be maintained
Marko Widgets allows DOM nodes to be preserved by putting a special w-preserve
, w-preserve-if(<condition>)
, w-preserve-body
or w-preserve-body-if(<condition>)
attribute on the HTML tags that should be preserved. Preserved DOM nodes will be reused and re-inserted into a widget's newly rendered DOM automatically.
<div w-bind><span w-preserve><p>The root span and all its children will neverbe re-rendered.</p><p>Rendered at .</p></span><div w-preserve-body>Only the children of the div will preserved andthe outer HTML div tag will be re-rendered.</div>Don't rerender the search results if no search resultsare provided.<app-search-results items="data.searchResults"w-preserve-ifdata.searchResults == null/></div>
Preserving DOM Attributes during Re-render
Similar to preserving DOM nodes, Marko Widgets also makes it possible to preserve specific attributes on a DOM node. This can be helpful if a separately library is modifying DOM attributes and those changes should be preserved during a rerender. This is mostly the case with class
and style
attributes when using a animation/tweening engines such as Velocity.js or GSAP.
The w-preserve-attrs
attribute can be applied to any DOM element and it expects a comma-separated list of attribute names as shown below:
<div w-preserve-attrs="class,style">...</div>