Knockout.js Templates — Named Templates and Dynamic Rendering
In this tutorial, you will learn about Knockout.js Templates. We cover key concepts, practical examples, and best practices to help you master this topic.
Knockout.js template binding renders named HTML templates with a specified data context, enabling reusable markup fragments and dynamic content switching without a component registration.
What You'll Learn
- Defining named templates with script tags
- Using the template binding with dynamic data
- Passing template arguments
- Template composition and nesting
- Server-rendered template integration
Why It Matters
While components are the modern approach, templates offer lightweight reuse without the overhead of component registration. They are ideal for rendering API responses, markdown content, or conditional layouts that do not need full component isolation.
Real-World Use
An email application where each email type (plain text, HTML, with attachments) uses a different template. The inbox list uses a template binding with the email type to choose the correct rendering template dynamically.
Template Flow
flowchart TD
A[Template Binding] --> B{template source}
B -->|element ID| C[Find DOM Template]
B -->|string| D[Use Template Engine]
B -->|function| E[Dynamic Selection]
C --> F[Clone Template Nodes]
D --> F
E --> F
F --> G[Bind to Data Context]
G --> H[Insert into DOM]
style A fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px
Defining Named Templates
Templates are defined in hidden <script> or <template> tags:
<!-- Using script tag (works in all browsers) -->
<script type="text/html" id="product-template">
<div class="product">
<h3 data-bind="text: name"></h3>
<p data-bind="text: description"></p>
<span class="price" data-bind="text: formattedPrice"></span>
</div>
</script>
<!-- Using template tag (modern browsers) -->
<template id="comment-template">
<div class="comment">
<strong data-bind="text: author"></strong>
<p data-bind="text: body"></p>
<small data-bind="text: date"></small>
</div>
</template>
Using the Template Binding
<div data-bind="template: { name: 'product-template', data: selectedProduct }"></div>
<!-- Or with shorthand (name only) -->
<div data-bind="template: 'product-template'"></div>
function ViewModel() {
var self = this;
self.products = ko.observableArray([
{ name: 'Laptop', description: 'High-performance laptop', price: 999 },
{ name: 'Mouse', description: 'Wireless mouse', price: 25 }
]);
self.selectedProduct = ko.observable(null);
self.selectProduct = function(product) {
self.selectedProduct(product);
};
}
Expected output: When selectedProduct is set to a product object, the template renders with that product as the binding context. The template's bindings reference properties on the product.
Template with Foreach
Templates work naturally with foreach for rendering lists:
<ul data-bind="template: { name: 'product-template', foreach: products }"></ul>
<!-- Alternative: foreach inside the template -->
<script type="text/html" id="product-list-template">
<li data-bind="foreach: products">
<div>
<strong data-bind="text: name"></strong>
<span data-bind="text: price"></span>
</div>
</li>
</script>
<div data-bind="template: { name: 'product-list-template', data: $data }"></div>
Dynamic Template Selection
Choose a template based on data type or state:
<script type="text/html" id="email-text">
<div class="email-plain">
<pre data-bind="text: content"></pre>
</div>
</script>
<script type="text/html" id="email-html">
<div class="email-html">
<iframe data-bind="attr: { srcdoc: content }"></iframe>
</div>
</script>
<script type="text/html" id="email-attachment">
<div class="email-attachment">
<p data-bind="text: content"></p>
<div data-bind="foreach: attachments">
<a data-bind="attr: { href: url }, text: filename"></a>
</div>
</div>
</script>
function ViewModel() {
var self = this;
self.emails = ko.observableArray([
{ type: 'text', content: 'Hello, this is a plain text email.' },
{ type: 'html', content: '<h1>Hello</h1><p>HTML email body</p>' },
{ type: 'attachment', content: 'See attached files.',
attachments: [{ filename: 'report.pdf', url: '/files/report.pdf' }] }
]);
self.emailTemplate = function(email) {
return 'email-' + email.type;
};
}
<div data-bind="template: { name: emailTemplate, foreach: emails }"></div>
Expected output: Each email renders using its corresponding template. The emailTemplate function selects the template name dynamically based on the email type property.
Template with Nested Templates
<script type="text/html" id="user-profile">
<div class="profile">
<h2 data-bind="text: displayName"></h2>
<div data-bind="template: { name: 'user-address', data: address }"></div>
<div data-bind="template: { name: 'user-contacts', data: contacts }"></div>
</div>
</script>
<script type="text/html" id="user-address">
<div class="address">
<p data-bind="text: street"></p>
<p><span data-bind="text: city"></span>, <span data-bind="text: zip"></span></p>
</div>
</script>
Expected output: The user-profile template includes two nested template bindings that render with the address and contacts sub-objects as their respective data contexts.
Using afterRender Callback
<div data-bind="template: {
name: 'product-template',
foreach: products,
afterRender: function(elements, data) {
console.log('Rendered ' + data.name + ' for product:', elements);
}
}"></div>
Expected output: The afterRender callback fires after each item is rendered, receiving the DOM elements and the data item. This is useful for integrating with third-party libraries that need to manipulate the rendered DOM.
Template Engine Customization
Knockout allows custom template engines for different rendering sources:
// Using the native template engine (default)
ko.setTemplateEngine(new ko.nativeTemplateEngine());
// For server-side templates, create a custom engine
// that fetches templates from the server
Common Mistakes
Using
type="text/html"on script templates - Without the correct type attribute, the browser tries to execute the template as JavaScript. Always usetype="text/html".Not passing data to template - When using
template: 'templateName'withoutdata, the template uses the current binding context. If the context is wrong, the bindings may not resolve.Overusing templates instead of components - Templates lack lifecycle hooks and disposal. Use components for complex, self-contained widgets and templates for simple, reusable markup.
Template ID collisions - Template IDs share the global namespace. Prefix template IDs like
product-page-*to avoid collisions in large applications.Forgetting to close template tags - Unclosed template tags can silently fail or produce broken DOM. Always ensure proper nesting and closure.
Practice Questions
- How do you define a template that works in all browsers?
- What is the difference between
template: { name: 't1', data: item }andtemplate: { name: 't1', foreach: items }? - How do you dynamically select which template to use based on the data?
- What is the purpose of the
afterRendercallback? - When would you choose a template over a component?
Challenge: Build a product listing page that uses three different templates: one for featured products (large card), one for regular products (compact list), and one for out-of-stock products (grayed out). Use dynamic template selection based on product properties.
FAQ
Mini Project
Build a dashboard widget system where each widget type (chart, table, summary, feed) has its own template. Users can add and remove widgets, and each widget renders with its own data context using dynamic template selection.
What's Next
Data often comes from external APIs. Learn how the Knockout mapping plugin converts plain JSON objects into observable ViewModels automatically.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro