Lively / HTML Components — Part 3: Advanced Rendering, Templates & Reference¶
Series index:
Part 1 — Overview & Page Basics (
01-lively-overview-and-page-basics.md)Part 2 — Events, Forms & File Uploads (
02-lively-events-forms-file-uploads.md)Part 3 — Advanced Rendering, Templates & Reference (this file)
Pre-Rendering Components¶
Pre-rendering caches component outputs for faster loading.
Example:
from duck.html.components.page import Page
from duck.shortcuts import to_response
def home(request):
page = Page(request=request)
background_thread.submit_task(
lambda: page.pre_render(deep_traversal=True, reverse_traversal=True)
)
return to_response(page)
Counter App (Demo Blueprint)¶
Include the built-in counter blueprint to test:
BLUEPRINTS = [
"duck.etc.blueprints.counterapp.blueprint.CounterApp",
]
Visit /counterapp after adding it.
Notes:
Component responses maximize the benefits of Lively Component System.
Fast navigation works best with component responses in views.
Components in Templates¶
Components can be used in Jinja2 and Django templates.
Jinja2 Example:
{{ Button(
id="btn",
text="Hello world",
)
}}
Django Example:
{% Button %}
id="btn",
text="Hello world",
{% endButton %}
Only add components to
TEMPLATE_HTML_COMPONENTSinsettings.pyfor template usage.
Custom Components¶
from duck.html.components import InnerComponent
from duck.html.components.button import Button
from duck.shortcuts import to_response
class MyComponent(InnerComponent):
def get_element(self):
return "div"
def on_create(self):
super().on_create()
self.add_child(Button(text="Hi there"))
def home(request):
comp = MyComponent(request=request)
return to_response(comp)
Component Extensions¶
Extensions enhance component functionality.
from duck.html.components.button import Button
from duck.html.components.extensions import Extension
class MyExtension(Extension):
def apply_extension(self):
super().apply_extension()
self.style["background-color"] = "red"
class MyButton(MyExtension, Button):
pass
btn = MyButton() # Button with background-color "red"
Notes:
By default, all Lively components come with 2 built-in component extensions:
BasicExtension: The basic extension which enables setting attributes like
color,bg_color, and more, to actually alter the componentstyle.StyleCompatibilityExtension: Enables compatibility of style properties — e.g. setting
backdrop-filterwill also set-webkit-backdrop-filterand other compatibility style properties.
Predefined Components¶
All predefined components are available in duck.html.components and in the default SETTINGS['TEMPLATE_HTML_COMPONENTS'].
Use these components to rapidly build responsive, interactive UIs in Duck.
Force Updates on Lively Components¶
It is possible to modify values set with Javascript with the use of Force updates or the ws.update_now method.
The following example showcases this technique:
from duck.html.components import ForceUpdate
from duck.html.components.button import Button
from duck.html.components.script import Script
def on_btn_click(btn, *_):
# Return a force update to reset btn text to the initial text set on btn
# First argument to ForceUpdate is the component and second is list of updates.
# List of updates are "text"/"inner_html", "props", "style" and "all".
return ForceUpdate(btn, ["text"])
def home(request):
# Add a javascript callback to click event.
# This always gets called first before Duck event callback.
btn = Button(text="Click me", id="btn", props={"onclick": "btnClick()"})
# Add some Javascript
script = Script(
inner_html="""
function btnClick() {
const btn = document.getElementId(`btn`);
btn.textContent = "Clicking..";
}
"""
)
# Add script to button
btn.add_child(script)
# Bind click event to python callable.
btn.bind("click", on_btn_click, update_self=False)
# Return btn, will be converted to response by Duck.
return btn
Critical rule: Lively performs non-destructive syncing.
It only updates props and styles it is aware of from the server state, and ignores anything it didn’t create or track.
New props/styles set from Python (e.g. in event handlers) will sync normally.
Props/styles added externally (e.g. via
execute_js) are not tracked.Untracked properties will not be removed or overridden, even when patches are applied.
This means Lively will merge updates, not replace the entire prop/style object.
# Initial render — no background-color
btn = Button(text="Click")
# Later in Python (event handler) — this WILL sync
btn.style.update({"background-color": "red"})
// Added externally — Lively does NOT track this
element.style.border = "1px solid blue"
Even after future updates from Python:
btn.style.update({"background-color": "green"})
The border style will remain untouched because Lively never tracked it.
Key Takeaway¶
Lively behaves like a partial diff system, not a full state replacement system:
✅ Updates known fields
✅ Adds new fields from server-side changes
❌ Does not remove unknown/external fields
Note: In cases where a property’s presence resolves to
trueregardless of its value — e.g. the presence ofdisabledortrue— you can just execute JavaScript (usingexecute_js) to alter the component directly.
Immediate Syncing (update_now)¶
Note
Added in version 1.1.0
Method update_now synchronizes the current component state with the client immediately, without waiting for the dispatch loop’s final VDOM diff. Unlike deferred updates, this applies changes right away and can be safely called within a component event handler — useful when the client needs to reflect an intermediate state before the handler continues, restores state, or performs longer-running work.
Note
This method internally performs a ForceUpdate, sending the specified updates unconditionally — even if the resulting state is identical to what the client already has. Use this when you know a change occurred and want to guarantee it reaches the client without paying for a diff.
Example:
# Immediately sync state before continuing execution
async def on_click(btn, _, __, ws):
btn.text = "Clicking..."
await ws.update_now(btn, updates=["text"])
# Continue processing after UI reflects the change
btn = Button(text="Click me")
btn.bind("click", on_click, update_self=True)
Diffed Immediate Syncing (sync_now)¶
Note
Added in version 2.3.0
Method sync_now also synchronizes state with the client immediately from within an event handler, but unlike update_now, it diffs the component against its own last checkpoint first and sends only the patches that actually changed — no patch is sent for a no-op update. It is scoped to the target component’s own subtree, so syncing a deeply nested descendant (e.g. a button inside a page) never re-renders or re-diffs its ancestors.
Note
sync_now requires the target component to be a descendant of one of the event’s update_targets (or an update_target itself). Calling it on an unrelated component raises a ForceUpdateError.
Example:
# Diff and sync only if the state actually changed
async def on_click(btn, _, __, ws):
btn.text = "Clicking..."
await ws.sync_now(btn)
# Continue processing after UI reflects the change, with no
# patch sent at all if btn.text ends up unchanged by handler's end
btn = Button(text="Click me")
btn.bind("click", on_click, update_self=True)
sync_now vs update_now¶
|
|
|
|---|---|---|
Sends a patch |
Always, unconditionally |
Only if state actually changed |
Cost |
Skips diffing — cheaper per call if you’re certain state changed |
Diffs first — avoids wasted network writes on no-ops |
Scope |
Whatever component/updates you pass |
Automatically scoped to the target’s own subtree |
Best for |
Small, definitely-changed components where a diff isn’t worth the CPU |
Any case with risk of a no-op, or when targeting a nested descendant without touching its ancestors |
Component Lifecycle¶
Whenever each component is created or added to the component tree, the following methods are executed:
on_root_finalized¶
Called whenever a root component is finalized, meaning it is never going to change. Use this for doing tasks on root components, e.g. using document_bind on Page components.
Args:
root: This is the root component.
on_parent¶
Called whenever a child component is added to a parent component.
Args:
parent: The parent component.
Other Points to Note¶
Duck only keeps track of
props(attributes) andstyle(style attributes) that are initially set using Duck. This means that Duck will not touch any prop or style attribute set solely using Javascript that is never declared on Lively components.The
valueargument passed to a Duck event handler is variable and can be any datatype depending on the event.Component traversal can be done using the
root,parent, orchildrenproperties.JS execution is done asynchronously, so all code that is passed in
LivelyWebSocketView.execute_jsorget_js_resultis awaited by default.DOM mutation/updates using Javascript — like moving, adding, or removing components without using Lively — will cause issues and, by default, will be flagged in the browser.