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:

  1. Finds the document and its schema ("Code example")
  2. Looks for #/Code example.hash
  3. Merges it with the master page #/master.hash
  4. Binds all #FieldName# placeholders with the document data
  5. 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