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 `name: {{name}}`.
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 ""
.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 files 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/', 'views/partials/'],
extname: '.liquid'
Relative Paths

Relative paths in root will be resolved against cwd().

When {% render "foo" %} is renderd or liquid.renderFile('foo') is called, the following files will be looked up and the first existing file will be used:

  • cwd()/views/foo.liquid
  • cwd()/views/partials/foo.liquid

If none of the above files exists, an ENOENT error will be throwed. Here’s a demo for Node.js: demo/nodejs.

When LiquidJS is used in browser, say current location is, only the first root will be used and the file to be fetched is:

If fetch fails, a 404/500 error or network failures for example, an ENOENT error will be throwed.
Here’s a demo for browsers: demo/browser.

Abstract File System

LiquidJS defines an abstract file system interface in src/fs/ifs.ts and the default implementation is src/fs/node.ts for Node.js and src/fs/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
exists () {
return true
contains () {
return true
resolve(root, file, ext) {
return file
Path Traversal Vulnerability

The default value of contains() always returns true. That means when specifying an abstract file system, you’ll need to provide a proper contains() to avoid expose such vulnerabilities.