Create display templates for your schemas Last updated: 06. Mar 2026
Display templates are at the core of how Docly renders your documents as web pages. A .hash file defines the HTML output for all documents of a given schema. This guide explains everything you need to know — from placement and naming, to data binding, loops, master pages, images, and more.
What is a display template?
A display template is a .hash file that transforms a JSON document into HTML. When a visitor opens a document on your site, Docly finds the matching display template and uses it to render the page.
For example, if you have a schema called Blog post, Docly will look for a file called #/Blog post.hash to determine how all Blog post documents should be displayed.
Without a display template, your documents have no visual output — they are just raw JSON data.
Important: Display templates must always be placed directly in the
#folder (the hash folder). For example:#/Blog post.hash. If you place them in a subfolder like#/Templates/Blog post.hash, they will not be picked up by Docly.
Naming convention
The filename of the .hash file must match the schema name exactly. The format is:
#/[Schema name].hash
Examples:
Schema name: "Employee" → File: #/Employee.hash
Schema name: "Blog post" → File: #/Blog post.hash
Schema name: "Code example" → File: #/Code example.hash
Schema name: "Documentation.5" → File: #/Documentation.5.hash This site (developers.docly.net) uses several display templates. For instance, #/Code example.hash renders all documents with the "Code example" schema, and #/Documentation.5.hash renders the documentation pages you are reading right now.
Simple data binding with #FieldName#
The most fundamental feature of #JS is data binding. Use #FieldName# to output the value of a field from your document.
Suppose your schema has fields Name, Number, and Email, and a document contains this data:
{
"Name": "John Doe",
"Number": 42,
"Email": "john@example.com"
} Your display template can render this like so:
<h1>#Name#</h1>
<p>Employee number: #Number#</p>
<a href="mailto:#Email#">Send email to #Name#</a> Output:
<h1>John Doe</h1>
<p>Employee number: 42</p>
<a href="mailto:john@example.com">Send email to John Doe</a> It's that simple — every #FieldName# is replaced with the corresponding value from the document.
Code blocks and expressions
#JS has two distinct ways to embed JavaScript:
1. Code blocks #{...}# — for multi-line code where you need to declare variables, perform calculations, or write more complex logic. These blocks do not output anything directly.
2. Single expressions #expression# — for outputting a value, like #Name# or #item.Title.toUpperCase()#. This also applies to control flow statements like #if(...){}# and #for(...){#.
Code block example — declare variables and run logic:
#{
let greeting = Name ? 'Hello, ' + Name + '!' : 'Hello, stranger!';
let level = Number > 100 ? 'Senior' : 'Junior';
}#
<h1>#greeting#</h1>
<p>Level: #level#</p> Control flow with #if# / #for# — these use single # delimiters, not code blocks:
#if (Number > 100) {#
<span class="badge bg-success">Senior employee</span>
#} else {#
<span class="badge bg-info">Employee</span>
#}# Notice the difference: #{...}# wraps a multi-line code block, while #if(...){# and #for(...){# are single statements using plain # delimiters. You can freely mix HTML between the opening and closing statements.
Looping over arrays
If your schema contains arrays (lists of items), you can loop over them using for...of inside #{...}# blocks.
Given this data:
{
"Numbers": [1, 2, 3],
"Objects": [
{ "Name": "test 1" },
{ "Name": "test 2" },
{ "Name": "test 3" }
]
} You can loop like this:
<b>Numbers:</b>
#for(let number of Numbers) {#
<a>#number#</a>
#}#
<b>Objects:</b>
#for(let obj of Objects) {#
<div>#obj.Name#</div>
#}# Output:
<b>Numbers:</b>
<a>1</a>
<a>2</a>
<a>3</a>
<b>Objects:</b>
<div>test 1</div>
<div>test 2</div>
<div>test 3</div> Notice how you access properties on each item using dot notation: #obj.Name#.
Linking images and files
If your schema has image or file fields, you can link to them in your template.
Images: Use the linkimage() function to generate an optimized image URL. The parameters are: field name, width, height, and quality (1-3).
<!-- Single image field called "Image1" -->
<img src="#docly.linkImage(Image1,1024,768,2)#" alt="An image" /> Files: Use the docly.linkFile() function to generate a download URL. The parameters are: field name and optional filename:
<!-- File field called "File1" -->
<a href="#docly.linkFile(File1)#">Download file</a> Inside loops: When looping over arrays that contain images or files, prefix with the loop variable:
#for (let item of Items) {#
<div>
<img src="#docly.linkImage(item.Image1,1024,768,2)#" alt="An image" />
<a href="#docly.linkFile(item.File1)#">Download</a>
</div>
#}# Images from other documents: Use file.Url with additional parameteres to return embedded file or image. Image example:
#for (let file of docly.getFiles('Blog')) {#
<div>
<img src="#file.Url#/#file.Image1#/800x600x2/image.jpg#" alt="Blog image" />
</div>
#}# File example:
#for (let file of docly.getFiles('Blog')) {#
<div>
<a src="#file.Url#/#file.File1##" target="_blank">Download file</a>
</div>
#}# Using master pages
Most websites share a common layout — header, footer, navigation, etc. Instead of repeating this in every template, you can define a master page and have your display templates inherit from it.
A master page is also a .hash file placed in the # folder. It contains the shared HTML structure with placeholder elements that child templates replace.
Master page example (#/master.hash):
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Site</title>
<link rel="stylesheet" href="/assets/style.css">
</head>
<body>
<header>
<h1>My Website</h1>
<nav>...</nav>
</header>
<main id="content">
<!-- Child templates replace this -->
</main>
<footer>
<p>© 2024 My Website</p>
</footer>
</body>
</html> Child template example (#/Blog post.hash):
<!--#master file="#/master.hash" -->
<html lang="en">
<head>
<title xdt:Transform="Replace">#Title# - My Site</title>
</head>
<body>
<main id="content" xdt:Transform="Replace">
<article>
<h1>#Title#</h1>
<p class="lead">#Introduction#</p>
<div>#Body#</div>
</article>
</main>
</body>
</html> Key points:
- The first line
<!--#master file="#/master.hash" -->tells Docly which master page to use. - Use
xdt:Transform="Replace"on elements to replace the corresponding element in the master page (matched by tag name and id). - Use
xdt:Transform="Remove"to remove an element from the master page that you don't want in this template.
Include files
You can split your templates into reusable parts and include them using <!--#include -->:
<!--#include file="#/Header.hash" -->
<!--#include file="#/Sidenav.hash" -->
<!--#include file="#/Footer.hash" --> This is useful for navigation menus, sidebars, footers, or any reusable component. The included files also have access to the same document data and can use all #JS features.
The request object
Inside your templates, the request object gives you access to useful metadata about the current page and request:
<!-- Page title (document filename without extension) -->
<title>#request.title#</title>
<!-- Current URL -->
<a href="#request.Url#">Link to this page</a>
<!-- Current folder path -->
<p>You are in: #request.folderpath#</p> A complete example from this site
This very developer site uses display templates extensively. Here is a simplified version of how the Code example schema template works.
The schema (#/Code example.docly) defines fields like Description, Data, and Code. The template (#/Code example.hash) renders them:
<!--#master file="#/master.hash" -->
<html lang="en">
<head>
<title xdt:Transform="Replace">#request.title# - Docly™ dev</title>
</head>
<body>
<main id="main" xdt:Transform="Replace">
<div class="docs-wrapper">
<div class="docs-content">
<div class="container">
<h1>#request.title#</h1>
<p>#Description#</p>
<h3>Data</h3>
<pre><code>#Data#</code></pre>
<h3>Code</h3>
<pre><code>#Code#</code></pre>
</div>
</div>
</div>
</main>
</body>
</html> When you open a Code example document (like Simple binding), Docly automatically:
- Finds the document and its schema ("Code example")
- Looks for
#/Code example.hash - Merges it with the master page
#/master.hash - Binds all
#FieldName#placeholders with the document data - Sends the final HTML to the browser
Summary and checklist
| Rule | Details |
|---|---|
| Placement | Always in the # folder directly — not in a subfolder |
| Filename | Must match the schema name exactly: #/[Schema name].hash |
| Simple binding | Use #FieldName# to output field values |
| Code blocks | Use #{...}# for multi-line code (variables, logic) |
| Expressions & control flow | Use #expression# for single expressions, or #if(...){# / #for(...){# for control flow |
| Arrays | Use #for(let item of Items) {# ... #}# |
| Images | Use #docly.linkImage(FieldName,width,height,quality)# |
| Files | Use #docly.linkFile(FieldName)# |
| Master pages | Use <!--#master file="#/master.hash" --> |
| Includes | Use <!--#include file="#/filename.hash" --> |
Related resources
- Simple binding example — basic field output
- Binding arrays example — looping over lists
- Linking images and files in hash — working with media
- Menu with active class — navigation in templates