Advanced SSR FULL
Server-side generation, computed properties, form handling, and nested data structures. For basic SSR setup, see Server-Side Rendering (SSR). For lifecycle, architecture, and troubleshooting, see SSR Internals.
Server-Side Generation
To use SSR, your server needs to produce HTML that WildflowerJS can hydrate. The key rules are:
- Add
data-ssr="true"to the component root element - Include
data-bindattributes on elements that map to state properties - Include a
<template>inside eachdata-listfor future client-side additions - Use
data-typefor non-string values (number, boolean, json)
Node.js / Express
function renderUserDashboard(userData) {
return `
<div data-component="user-dashboard" data-ssr="true">
<h2 data-bind="name">${escapeHtml(userData.name)}</h2>
<p data-bind="email">${escapeHtml(userData.email)}</p>
<div data-list="notifications">
<template>
<div class="notification">
<span data-bind="message"></span>
<time data-bind="timestamp"></time>
</div>
</template>
${userData.notifications.map(notif => `
<div class="notification">
<span data-bind="message">${escapeHtml(notif.message)}</span>
<time data-bind="timestamp">${escapeHtml(notif.timestamp)}</time>
</div>
`).join('')}
</div>
</div>
`;
}
// Express route
app.get('/dashboard', async (req, res) => {
const userData = await getUserData(req.user.id);
const html = renderUserDashboard(userData);
res.send(pageTemplate(html));
});
PHP
<?php
function renderProductCatalog($products) {
$items = '';
foreach ($products as $p) {
$name = htmlspecialchars($p['name']);
$price = number_format($p['price'], 2);
$items .= "<div class=\"product\">
<strong data-bind=\"name\">{$name}</strong>
<span data-bind=\"price\" data-type=\"number\">\${$price}</span>
</div>";
}
return '<div data-component="product-catalog" data-ssr="true">
<h1 data-bind="title">Product Catalog</h1>
<div data-list="products">
<template>
<div class="product">
<strong data-bind="name"></strong>
<span data-bind="price" data-type="number"></span>
</div>
</template>' . $items . '
</div>
</div>';
}
?>
Python / Django
# Django template (todos/templates/todo_list.html)
<div data-component="todo-list" data-ssr="true">
<h2>My Todos</h2>
<div data-list="todos">
<template>
<div class="todo-item">
<input type="checkbox" data-model="completed" data-type="boolean">
<span data-bind="text"></span>
</div>
</template>
{% for todo in todos %}
<div class="todo-item">
<input type="checkbox"
data-model="completed"
data-type="boolean"
{% if todo.completed %}checked{% endif %}>
<span data-bind="text">{{ todo.text }}</span>
</div>
{% endfor %}
</div>
</div>
{{ }}, PHP htmlspecialchars()) is your server-side templating. WildflowerJS itself does not use template syntax.
Computed Properties with SSR
The server can pre-render computed values so they appear instantly. When the client activates, computed properties take over and recalculate reactively.
Server-Rendered Computed Values:
<div data-component="order-summary" data-ssr="true">
<!-- Server pre-renders the computed totals -->
<p>Subtotal: $<span data-bind="subtotal">223.94</span></p>
<p>Tax: $<span data-bind="tax">22.39</span></p>
<p>Total: $<span data-bind="total">246.33</span></p>
</div>
wildflower.component('order-summary', {
state: {
items: [
{ name: 'Wireless Headphones', price: 79.99, quantity: 2 },
{ name: 'USB-C Cable', price: 12.99, quantity: 3 },
{ name: 'Phone Case', price: 24.99, quantity: 1 }
],
taxRate: 0.10
},
computed: {
subtotal() {
return this.items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
).toFixed(2);
},
tax() {
return (parseFloat(this.subtotal) * this.taxRate).toFixed(2);
},
total() {
return (parseFloat(this.subtotal) + parseFloat(this.tax)).toFixed(2);
}
}
});
Form Handling
SSR forms require data-model (not data-bind) for inputs. Checkboxes need data-type="boolean" so the framework parses checked state correctly.
Server-Rendered Todo with Form State:
<div data-list="todos">
<template>
<div class="todo-item">
<input type="checkbox" data-model="completed" data-type="boolean">
<span data-bind="text"></span>
</div>
</template>
<!-- Server-rendered with checkbox state -->
<div class="todo-item">
<input type="checkbox" data-model="completed"
data-type="boolean" checked>
<span data-bind="text">Set up SSR for landing page</span>
</div>
<div class="todo-item">
<input type="checkbox" data-model="completed"
data-type="boolean">
<span data-bind="text">Write unit tests</span>
</div>
</div>
checked attribute on the checkbox is parsed by the SSR system as completed: true. Use data-model for inputs and data-type="boolean" for checkboxes.
Form Rules
| Input Type | Attribute | Server HTML | Parsed State |
|---|---|---|---|
| Text input | data-model |
<input data-model="name" value="John"> |
"John" |
| Checkbox | data-model + data-type="boolean" |
<input type="checkbox" data-model="done" data-type="boolean" checked> |
true |
| Checkbox (unchecked) | data-model + data-type="boolean" |
<input type="checkbox" data-model="done" data-type="boolean"> |
false |
| Select | data-model |
<select data-model="color"><option selected>Blue</option></select> |
"Blue" |
data-bind instead of data-model on form inputs. In SSR mode, data-bind reads textContent, while data-model reads the input's value or checked property.
Nested Data Structures
For complex objects that can't easily be represented as flat DOM values, use a <script type="application/json" data-ssr-state> block:
<div data-component="user-card" data-ssr="true">
<!-- Hidden JSON block for complex nested state -->
<script type="application/json" data-ssr-state>
{
"user": {
"id": 123,
"profile": {
"name": "John Doe",
"avatar": "/avatars/john.jpg",
"preferences": {
"theme": "dark",
"notifications": true
}
}
}
}
</script>
<!-- Visible content uses nested binding paths -->
<img data-bind="user.profile.avatar" src="/avatars/john.jpg" alt="Avatar">
<h3 data-bind="user.profile.name">John Doe</h3>
<p>Theme: <span data-bind="user.profile.preferences.theme">dark</span></p>
</div>
The SSR system merges the JSON block into the component's state, giving you deep reactivity on nested properties.
When to Use Each Approach
| Approach | Use When | Example |
|---|---|---|
Inline data-bind |
Flat values visible in the DOM | <span data-bind="name">John</span> |
| Nested path | Shallow nesting, values visible | <span data-bind="user.name">John</span> |
data-ssr-state JSON |
Deep objects, config data, non-visible values | Preferences, metadata, feature flags |