Files
ed15a52c26 docs: revisit wording & style for liquidjs.com (#906)
* docs: polish theme, playground, and reference pages

Improve readability of the docs site with updated light/dark tokens, shared
code-block styling, and playground editors that follow system color scheme.
Skip CookieHub on localhost, serve the browser bundle from theme source, and
use backtick titles on filter/tag reference pages for consistent navigation.

Co-authored-by: Cursor <[email protected]>

* docs: highlight npx in bash blocks and polish English copy

Use Prism insertBefore for CLI commands like npx, tighten tutorial and reference wording, and keep YAML titles free of backticks so sidebar and page headings stay correct.

Co-authored-by: Cursor <[email protected]>

* docs: restore lowercase filter and tag titles

Titles should match actual filter/tag identifiers (e.g. abs, append), not capitalized English labels.

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-06-08 00:26:52 +08:00

4.7 KiB

title
title
Render Files

For a typical project there could be a directory of template files, you'll need to set the template root and call renderFile or renderFileSync to render a specific file.

Render a File

For example you have a directory of templates like this:

.
├── index.js
└── views/
  ├── hello.liquid
  └── world.liquid

hello.liquid contains a single line {%raw%}name: {{name}}{%endraw%}. Now save the following contents into index.js:

var engine = new Liquid({
    root: path.resolve(__dirname, 'views/'),  // root for layouts/includes lookup
    extname: '.liquid'          // used for layouts/includes, defaults ""
});
engine
    .renderFile("hello", {name: 'alice'})   // will read and render `views/hello.liquid`
    .then(console.log)  // outputs "Alice"

Run node index.js and you'll get output like this:

> node index.js
name: alice

Template Lookup

Template file names passed to renderFile, parseFile, renderFileSync, parseFileSync APIs, and include, layout tags are resolved against the root option.

It can be a string-typed path (see above example), or a list of root directories, in which case templates will be looked up in that order. e.g.

var engine = new Liquid({
    root: ['views/'],
    partials: ['views/partials/'],
    layouts: ['views/layouts/'],
    extname: '.liquid'
});

{% note tip Relative Paths %}Relative paths in root will be resolved against cwd().{% endnote %}

  • When parse(), render() functions are called, for example liquid.renderFile('foo'), templates under root will be looked up.
  • When a partial is requested, for example {% raw %}{% render "foo" %}{% endraw %}, templates under partials will be looked up.
  • When a layout is requested, for example {% raw %}{% layout "foo" %}{% endraw %}, templates under layouts will be looked up.

When LiquidJS is used in browser, the paths will be resolved based on current location. Here's a demo for browsers: demo/browser.

Abstract File System

LiquidJS defines an abstract file system interface and the default implementation is src/fs/fs-impl.ts for Node.js and src/build/fs-impl-browser.ts for the browser bundle.

The Liquid constructor provides a fs option to specify the file system implementation. It's supposed to be used to define customized template fetching logic, i.e. fetch template from a database table, like:

var engine = new Liquid({
    fs: {
        readFileSync (file) {
            return db.model('Template').findByIdSync(file).text
        },
        async readFile (file) {
            const template = await db.model('Template').findById(file)
            return template.text
        },
        existsSync () {
            return true
        },
        async exists () {
            return true
        },
        contains () {
            return true
        },
        resolve(root, file, ext) {
            return file
        }
    }
});

{% note warn Path Traversal Vulnerability %}The built-in Node fs implements contains() with realpath so templates cannot escape the root via symlinks. The browser bundle omits contains (loader treats paths as allowed). For a custom abstract fs, implement contains unless every resolved path is trusted.{% endnote %}

In-memory Template

To facilitate rendering without files, there's a templates option to specify a mapping of filenames and their content. LiquidJS will read templates from the mapping.

const engine = new Liquid({
  templates: {
    'views/entry': 'header {% include "../partials/footer" %}',
    'partials/footer': 'footer'
  }
})
engine.renderFileSync('views/entry'))
// Result: 'header footer'

Note that file system options like root, layouts, partials, relativeReference will be ignored when templates is specified.