Files
liquidjs/tutorials/differences.html
T

214 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head prefix="og: https://ogp.me/ns#">
<meta charset="utf-8">
<title>Differences with Shopify/liquid | LiquidJS</title>
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="LiquidJS is a simple, expressive, and safe template engine for JavaScript, compatible with Shopify and GitHub Pages.">
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net/">
<link rel="manifest" href="/manifest.json">
<!-- Canonical links -->
<link rel="canonical" href="https://liquidjs.com/tutorials/differences.html">
<!-- Alternative links -->
<link rel="alternative" hreflang="en" href="https://liquidjs.com/tutorials/differences">
<!-- Icon -->
<link rel="apple-touch-icon" sizes="57x57" href="../icon/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="../icon/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="../icon/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="../icon/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="../icon/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="../icon/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="../icon/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="../icon/apple-touch-icon-152x152.png">
<link rel="icon" type="image/png" href="../icon/favicon-196x196.png" sizes="196x196">
<link rel="icon" type="image/png" href="../icon/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="../icon/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="../icon/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="../icon/favicon-32x32.png" sizes="32x32">
<meta name="msapplication-TileColor" content="#2f83cd">
<meta name="msapplication-TileImage" content="../icon/mstile-144x144.png">
<link rel="stylesheet" href="../css/navy.css">
<link rel="alternate" href="../atom.xml" title="LiquidJS" type="application/atom+xml">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orestbida/[email protected]/dist/cookieconsent.css">
<script src="https://cdn.jsdelivr.net/gh/orestbida/[email protected]/dist/cookieconsent.umd.js"></script>
<script src="../js/cookieconsent-config.js"></script>
<meta name="generator" content="Hexo 7.3.0"></head>
<body>
<div id="container">
<header id="header" class="wrapper">
<div id="header-inner" class="inner">
<h1 id="logo-wrap">
<a href="../index.html" id="logo">LiquidJS</a>
</h1>
<nav id="main-nav">
<a href="../tutorials/intro-to-liquid.html" class="main-nav-link">Tutorials</a><a href="../tags/overview.html" class="main-nav-link">Tags</a><a href="../filters/overview.html" class="main-nav-link">Filters</a><a href="../playground.html" class="main-nav-link">Playground</a><a href="../api/classes/Liquid.html" class="main-nav-link">API</a>
<div id="search-input-wrap">
<i id="search-input-icon" class="icon-search"></i>
<input type="search" id="search-input" placeholder="Search...">
</div>
</nav>
<div class="main-nav-link icon-nav-link github-buttons-wrap">
<span class="gh-star">
<a class="gh-btn" href="https://github.com/harttle/liquidjs" target="_blank" rel="noopener external nofollow noreferrer" aria-label="Star harttle/liquidjs on GitHub">
<svg class="gh-btn-icon" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg>
<span>Star</span>
</a>
<a class="gh-count" id="gh-star-count" href="https://github.com/harttle/liquidjs" target="_blank" rel="noopener external nofollow noreferrer" aria-label="harttle/liquidjs stargazers"></a>
</span>
<a class="gh-btn gh-sponsor" href="https://github.com/sponsors/harttle" target="_blank" rel="noopener external nofollow noreferrer" aria-label="Sponsor @harttle on GitHub">
<svg class="gh-btn-icon" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg>
<span>Sponsor</span>
</a>
</div>
<a id="mobile-nav-toggle">
<span class="mobile-nav-toggle-bar"></span>
<span class="mobile-nav-toggle-bar"></span>
<span class="mobile-nav-toggle-bar"></span>
</a>
</div>
</header>
<div id="content-wrap">
<div id="content" class="wrapper">
<div id="content-inner">
<aside id="sidebar" role="navigation">
<div class="inner">
<strong class="sidebar-title">Getting Started</strong><a href="intro-to-liquid.html" class="sidebar-link">Intro to Liquid</a><a href="setup.html" class="sidebar-link">Setup</a><a href="options.html" class="sidebar-link">Options</a><a href="render-file.html" class="sidebar-link">Render Files</a><a href="partials-and-layouts.html" class="sidebar-link">Includes and Layouts</a><a href="use-in-expressjs.html" class="sidebar-link">Use in Express.js</a><strong class="sidebar-title">Advanced</strong><a href="caching.html" class="sidebar-link">Caching</a><a href="escaping.html" class="sidebar-link">Escaping</a><a href="register-filters-tags.html" class="sidebar-link">Register Filters/Tags</a><a href="access-scope-in-filters.html" class="sidebar-link">Access Scope in Filters</a><a href="parse-parameters.html" class="sidebar-link">Parse Parameters</a><a href="render-tag-content.html" class="sidebar-link">Render Tag Content</a><a href="drops.html" class="sidebar-link">Liquid Drops</a><a href="sync-and-async.html" class="sidebar-link">Sync and Async</a><a href="whitespace-control.html" class="sidebar-link">Whitespace Control</a><a href="plugins.html" class="sidebar-link">Plugins</a><a href="operators.html" class="sidebar-link">Operators</a><a href="truthy-and-falsy.html" class="sidebar-link">Truthy and Falsy</a><a href="security-model.html" class="sidebar-link">Security Model</a><a href="static-analysis.html" class="sidebar-link">Static Analysis</a><strong class="sidebar-title">Miscellaneous</strong><a href="migrate-to-9.html" class="sidebar-link">Migrate to LiquidJS 9</a><a href="changelog.html" class="sidebar-link">Changelog</a><a href="differences.html" class="sidebar-link current">Differences with Shopify/liquid</a><a href="contribution-guidelines.html" class="sidebar-link">Contribution Guidelines</a>
</div>
</aside>
<article class="article-container" itemscope itemtype="https://schema.org/Article">
<div class="article-inner">
<div class="article">
<div class="inner">
<header class="article-header">
<h1 class="article-title" itemprop="name">Differences with Shopify/liquid</h1>
<a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/edit/master/docs/source/tutorials/differences.md" class="article-edit-link" title="Improve this doc"><i class="icon-pencil"></i></a>
</header>
<div class="article-content" itemprop="articleBody">
<h2 id="Compatibility" class="article-heading"><a href="#Compatibility" class="headerlink" title="Compatibility"></a>Compatibility<a class="article-anchor" href="#Compatibility" aria-hidden="true"></a></h2><p>Being compatible with the Ruby version is one of our priorities. Liquid language is originally <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/Shopify/liquid">implemented in Ruby</a> and used by Shopify and Jekyll (and thus GitHub Pages). As you can see its one of the most popular template engines in Ruby. There are lots of people using LiquidJS to serve their templates originally written for Shopify themes and Jekyll sites.</p>
<p>So “being compatible” means serving developers from Shopify and Jekyll well:</p>
<ul>
<li><strong>Well-formed Liquid template should work just fine in LiquidJS</strong>. For example, <code>forloop.index</code> should be 1-indexed, <code>nil</code> should be rendered as empty string rather than <code>undefined</code>, etc. Although some features (e.g. <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/issues/236">#236</a>) are not feasible in JavaScript, at least were trying to implement all the semantics of Liquid language.</li>
<li><strong>All filters and tags in <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/Shopify/liquid">shopify/liquid</a> are supposed to be built in LiquidJS</strong>. But not those business-logic specific tags/filters typically defined by Shopify platform. Those features should be maintained as <a href="./plugins.html#Plugin-List">plugins</a>. For filters/tags that are not business-logic specific, like <code>&#123;% layout %&#125;</code>, and extremely useful, feel free to file an issue.</li>
</ul>
<p>In the meantime, its now implemented in JavaScript, that means it has to be more powerful:</p>
<ul>
<li><strong>Async as a first-class citizen</strong>. Filters and tags can be implemented asynchronously by returning a <code>Promise</code>.</li>
<li><strong>Can also be synchronous</strong>. For scenarios that are not I/O intensive, rendering synchronously can be much faster. You can call synchronous APIs like <code>.renderSync()</code> as long as all the filters and tags in the template can be rendered synchronously. All built-in filters/tags support both sync and async render.</li>
<li><strong><a href="https://liquidjs.com/tutorials/render-file.html#Abstract-File-System">Abstract file system</a></strong>. Along with async feature, LiquidJS can be used to serve templates stored in Databases <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/discussions/414">#414</a>, on remote HTTP server <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/discussions/485">#485</a>, and so on.</li>
<li><strong>Additional tags and filters</strong> like <code>layout</code> and <code>json</code>, <code>inspect</code>, <code>where_exp</code>, <code>group_by</code>, etc., see below for details.</li>
</ul>
<h2 id="Differences" class="article-heading"><a href="#Differences" class="headerlink" title="Differences"></a>Differences<a class="article-anchor" href="#Differences" aria-hidden="true"></a></h2><p>Though were trying to be compatible with the Ruby version, there are still some differences:</p>
<ul>
<li>Truthy and Falsy. All values except <code>undefined</code>, <code>null</code>, <code>false</code> are truthy, whereas in Ruby Liquid all except <code>nil</code> and <code>false</code> are truthy. See <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/pull/26">#26</a>.</li>
<li>Number. In JavaScript we cannot distinguish or convert between <code>float</code> and <code>integer</code>, see <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/issues/59">#59</a>. And when applied <code>size</code> filter, numbers always return 0, which is 8 for integer in ruby, cause they do not have a <code>length</code> property.</li>
<li>Stringify: Weve aligned string coercion for primitive types. While some differences remain; for example, in Shopify/liquid, <code>strip</code> returns the “inspected” string of an input array, whereas in LiquidJS, the <code>strip</code> filter simply stringifies the input array <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/discussions/852">#852</a>.</li>
<li><a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/Shopify/liquid/wiki/Introduction-to-Drops">.to_liquid()</a> is replaced by <code>.toLiquid()</code></li>
<li><a target="_blank" rel="noopener external nofollow noreferrer" href="https://www.rubydoc.info/gems/liquid/Liquid/Drop">.to_s()</a> is replaced by JavaScript <code>.toString()</code></li>
<li>Iteration order for objects. The iteration order of JavaScript objects, and thus LiquidJS objects, is a combination of the insertion order for string keys, and ascending order for number-like keys, while the iteration order of Ruby Hash is simply the insertion order.</li>
<li>Sort stability. The <a href="https://liquidjs.com/filters/sort.html">sort</a> stability is also not defined in both shopify/liquid and LiquidJS, but its <a target="_blank" rel="noopener external nofollow noreferrer" href="https://v8.dev/features/stable-sort">considered stable</a> for LiquidJS in Node.js 12+ and Google Chrome 70+.</li>
<li>Trailing unmatched characters inside filters are allowed in shopify/liquid but not in LiquidJS. It means filter arguments without a colon like <code>{{ "a b" | split " "}}</code> will throw an error in LiquidJS. This is intended to improve Liquid usability, see <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/issues/208">#208</a> and <a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/issues/212">#212</a>.</li>
<li>LiquidJS has more tags/filters than <a target="_blank" rel="noopener external nofollow noreferrer" href="https://shopify.github.io/liquid/basics/introduction/">the Liquid language</a>:<ul>
<li>LiquidJS-defined tags: <a href="../tags/layout.html">layout</a>, <a href="../tags/render.html">render</a> and corresponding <code>block</code> tag.</li>
<li>LiquidJS-defined filters: <a href="https://liquidjs.com/filters/json.html">json</a>, group_by, group_by_exp, where_exp, jsonify, inspect, etc.</li>
<li>Tags/filters that dont depend on Shopify platform are borrowed from <a target="_blank" rel="noopener external nofollow noreferrer" href="https://shopify.dev/docs/api/liquid/tags">Shopify</a>.</li>
<li>Tags/filters that dont depend on Jekyll framework are borrowed from <a target="_blank" rel="noopener external nofollow noreferrer" href="https://jekyllrb.com/docs/liquid/filters/">Jekyll</a>.</li>
</ul>
</li>
<li>Some tags/filters behave differently: <a href="https://liquidjs.com/filters/date.html">date</a> filter, malformed tags (like duplicated <code>else</code>, extra args for <code>endif</code>) throw errors in LiquidJS. </li>
</ul>
</div>
<footer class="article-footer">
<time class="article-footer-updated" datetime="2026-06-21T17:35:06.753Z" itemprop="dateModified">Last updated: 2026-06-21</time>
<a href="changelog.html" class="article-footer-prev" title="Changelog"><i class="icon-chevron-left"></i><span>Prev</span></a><a href="contribution-guidelines.html" class="article-footer-next" title="Contribution Guidelines"><span>Next</span><i class="icon-chevron-right"></i></a>
</footer>
</div>
</div>
<aside id="article-toc" role="navigation">
<div id="article-toc-inner">
<div id="article-toc-inner-list">
<strong class="sidebar-title">Contents</strong>
<ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Compatibility"><span class="toc-text">Compatibility</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Differences"><span class="toc-text">Differences</span></a></li></ol>
</div>
<a href="#" id="article-toc-top">Back to Top</a>
</div>
</aside>
</div>
</article>
</div>
</div>
</div>
<footer id="footer" class="wrapper">
<div class="inner">
<div id="footer-copyright">
&copy; 2026 <a href="https://github.com/harttle/liquidjs/graphs/contributors" rel="external nofollow noreferrer" target="_blank">Harttle</a><br>
Documentation licensed under <a href="https://creativecommons.org/licenses/by/4.0/" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>.
</div>
<div id="footer-links">
<button type="button" class="footer-link cookie-preferences" data-cc="show-consentModal" title="Manage cookie preferences"><i class="icon-shield"></i></button>
<a href="https://twitter.com/harttleharttle" rel="external nofollow noreferrer" class="footer-link" target="_blank"><i class="icon-twitter"></i></a>
<a href="https://opencollective.com/liquidjs" rel="external nofollow noreferrer" class="footer-link" target="_blank"><i class="icon-opencollective"></i></a>
<a href="https://github.com/harttle/liquidjs" rel="external nofollow noreferrer" class="footer-link" target="_blank"><i class="icon-github"></i></a>
</div>
</div>
</footer>
</div>
<div id="mobile-nav-dimmer"></div>
<nav id="mobile-nav">
<div id="mobile-nav-inner">
<ul id="mobile-nav-list">
<a href="../tutorials/intro-to-liquid.html" class="mobile-nav-link">Tutorials</a><a href="../tags/overview.html" class="mobile-nav-link">Tags</a><a href="../filters/overview.html" class="mobile-nav-link">Filters</a><a href="../playground.html" class="mobile-nav-link">Playground</a><a href="../api/classes/Liquid.html" class="mobile-nav-link">API</a>
</ul>
<div class="mobile-sidebar-list">
<strong class="mobile-nav-title">Getting Started</strong><a href="intro-to-liquid.html" class="mobile-nav-link">Intro to Liquid</a><a href="setup.html" class="mobile-nav-link">Setup</a><a href="options.html" class="mobile-nav-link">Options</a><a href="render-file.html" class="mobile-nav-link">Render Files</a><a href="partials-and-layouts.html" class="mobile-nav-link">Includes and Layouts</a><a href="use-in-expressjs.html" class="mobile-nav-link">Use in Express.js</a><strong class="mobile-nav-title">Advanced</strong><a href="caching.html" class="mobile-nav-link">Caching</a><a href="escaping.html" class="mobile-nav-link">Escaping</a><a href="register-filters-tags.html" class="mobile-nav-link">Register Filters/Tags</a><a href="access-scope-in-filters.html" class="mobile-nav-link">Access Scope in Filters</a><a href="parse-parameters.html" class="mobile-nav-link">Parse Parameters</a><a href="render-tag-content.html" class="mobile-nav-link">Render Tag Content</a><a href="drops.html" class="mobile-nav-link">Liquid Drops</a><a href="sync-and-async.html" class="mobile-nav-link">Sync and Async</a><a href="whitespace-control.html" class="mobile-nav-link">Whitespace Control</a><a href="plugins.html" class="mobile-nav-link">Plugins</a><a href="operators.html" class="mobile-nav-link">Operators</a><a href="truthy-and-falsy.html" class="mobile-nav-link">Truthy and Falsy</a><a href="security-model.html" class="mobile-nav-link">Security Model</a><a href="static-analysis.html" class="mobile-nav-link">Static Analysis</a><strong class="mobile-nav-title">Miscellaneous</strong><a href="migrate-to-9.html" class="mobile-nav-link">Migrate to LiquidJS 9</a><a href="changelog.html" class="mobile-nav-link">Changelog</a><a href="differences.html" class="mobile-nav-link current">Differences with Shopify/liquid</a><a href="contribution-guidelines.html" class="mobile-nav-link">Contribution Guidelines</a>
</div>
</div>
<div id="mobile-button-list">
<a href="https://github.com/harttle/liquidjs" class="mobile-nav-link" rel="external" target="_blank"><i class="icon-github"></i></a>
<a href="https://opencollective.com/liquidjs" class="mobile-nav-link" rel="external" target="_blank"><i class="icon-opencollective"></i></a>
</div>
</nav>
<script src="../js/main.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
<script>
document.getElementById('search-input-wrap').classList.add('on');
docsearch({
appId: 'QJ35YOZTU4',
apiKey: '8c6cbb824b4c5023f0bb2ef29e228bef',
indexName: 'liquidjs',
inputSelector: '#search-input',
debug: false
});
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-GM713991QQ"></script>
<script type="text/plain" data-category="analytics">
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-GM713991QQ');
</script>
</body>
</html>