LiquidJS
A TypeScript Liquid template engine for Node.js, browsers, and the CLI — Shopify, Jekyll, and GitHub Pages compatible.
Repository Health
Technical Analysis
LiquidJS is a full reimplementation of Shopify’s Liquid template language in pure TypeScript, built to run identically in Node.js, in the browser (via a UMD/ESM bundle), and as a standalone CLI. It targets the same syntax used by Shopify themes, Jekyll, and GitHub Pages, so existing Liquid templates and filters generally port over without rewriting.
The engine exposes both async and sync render APIs backed by a single generator-based implementation per feature, so plugins, custom tags, and filters never need to be written twice for each execution mode. It supports partials and layouts with pluggable file-system resolution, an Express view-engine adapter, static template analysis (for extracting referenced variables/partials without executing a render), and a sandboxed evaluation model that avoids arbitrary JavaScript execution inside templates.
It’s the template engine underneath tools like Eleventy (11ty) and is used directly by Shopify’s own CLI and checkout tooling, alongside adopters like GitHub Docs, Kibana, and Directus — spanning static site generation, themeable emails, and safely rendering user- or merchant-authored templates in multi-tenant products.
What You Get
- A
Liquidengine class withparse/render/parseAndRendermethods in bothasyncandSyncvariants, all driven by one shared generator implementation per operation - The full standard Liquid tag set (
if,for,case,assign,capture,include,render,layout,tablerow,cycle, and more) plus the standard filter library (string, array, math, date, URL, HTML, base64, crypto) - Pluggable custom tags and filters via
registerTag/registerFilter, and aplugin()hook for packaging reusable extensions - Partial and layout resolution through a configurable
FSinterface, supporting custom file-system or virtual-file lookups instead of the Nodefsmodule - An Express view-engine adapter (
engine.express()) for dropping LiquidJS straight intoapp.set('view engine', ...) - Static analysis (
analyze/analyzeSync) that walks a parsed template to list referenced variables and partials without executing a render — useful for linting or dependency graphs - Typed error classes (
ParseError,RenderError,TokenizationError,UndefinedVariableError) that carry the offending token and surrounding template context in the message - Prebuilt browser bundles (UMD and ESM) alongside the Node.js build, so the same engine runs client-side without a bundler-specific build step
Common Use Cases
- Static site generators and build pipelines (e.g. Eleventy) rendering Markdown/HTML pages through Liquid layouts and includes
- Multi-tenant SaaS products letting merchants or customers author their own templates (emails, checkout pages, storefront themes) without exposing arbitrary JS execution
- Server-rendered Express applications using
.liquidviews via the built-in Express engine adapter - Porting or maintaining Shopify theme code, Jekyll sites, or GitHub Pages content in a Node.js/TypeScript toolchain
- Client-side template rendering in the browser for previewing or live-editing Liquid templates (e.g. the project’s own playground)
Under The Hood
Architecture
The engine is organized as a pipeline of clearly separated stages: src/tokens turns raw template strings into a token stream, src/parser (Parser/Tokenizer) turns tokens into a Template[] AST, and src/render (Render, Expression, Operators) walks that AST against a Context/Scope (src/context) to produce output through src/emitters. src/tags and src/filters register into a per-Liquid-instance registry (this.tags, this.filters) rather than a global one, so multiple engine instances can carry independent extension sets; src/fs abstracts partial/layout file resolution behind a FS/LookupType interface rather than hard-coding Node’s fs. The defining architectural choice, documented in the repo’s own contributor guide, is that every feature is written as a single function* generator consumed by either toPromise() (async driver) or toValueSync() (sync driver) — Liquid.render()/renderSync() and parseFile()/parseFileSync() are thin wrappers over the same generator body, eliminating the duplicate async/sync implementations common in comparable engines. Swapping this generator-driver core would ripple through essentially every tag, filter, and the Context value-resolution path, since all of them yield through it.
Tech Stack
The engine is authored entirely in TypeScript (target ES6, strict: true) with a single runtime dependency (commander, for the CLI binary) — the render/parse core has no external dependencies. Rollup (rollup.config.mjs) builds four output targets from one source tree: CommonJS and ESM for Node, plus UMD and browser ESM bundles, published together under dist/ with package.json’s browser field swapping in the browser build automatically for bundlers. TypeDoc generates API docs, and a separate Hexo-based documentation site (docs/, navy theme) is built and deployed alongside the library. The bundled CLI (bin/liquid.js) and an Express integration are both layered on top of the same public Liquid class rather than living in separate packages.
Code Quality
Testing is extensive and split by concern: Jest-based integration tests (test/integration) covering tags, filters, context, drops, and static analysis import from src/ against current TypeScript; a separate test/e2e suite imports only from the built dist/ package root, guaranteeing the published bundle behaves like the source. Error handling is explicit and typed rather than swallowed: a LiquidError base class captures the offending Token, the original wrapped error, and renders a message with surrounding template context, with dedicated subclasses (ParseError, RenderError, TokenizationError, UndefinedVariableError) rather than generic Error throws. ESLint (TypeScript-ESLint, eslint-plugin-deprecation) and strict-mode TypeScript run in CI alongside build, test, coverage, and a performance-regression check (perf:diff) on every pull request, and the project’s own contributor guide enforces minimal, narrowly scoped diffs and non-narrative code comments.
What Makes It Unique
The unified generator-based sync/async core is the standout technical decision: rather than hand-maintaining parallel synchronous and asynchronous code paths (a common source of drift in template engines that offer both), every tag and filter is implemented once as a generator and driven by whichever driver the caller needs, including for genuinely async operations like resolving a partial from a remote FS implementation. Combined with static analysis that extracts a template’s referenced variables and partials without rendering it, and a sandboxed evaluation model that never executes arbitrary host-language code, this makes LiquidJS well suited to the specific niche of safely rendering user-authored templates in multi-tenant systems — a harder requirement than most general-purpose JavaScript templating libraries take on.
Used by 13 apps in this directory
CourseLit
Ecommerce · Blogging
Open-source, self-hosted LMS for selling online courses, digital downloads, and building communities on your own branded website.
Directus
CMS · Low Code Platforms
Connect any SQL database and get instant REST and GraphQL APIs, a visual management Studio, and a native MCP server for AI agents — free for most organizations.
Dittofeed
Marketing · Automation
Open-source omni-channel customer engagement platform for automating transactional and marketing messages via email, SMS, WhatsApp, Slack, and mobile push.
Kibana
Analytics · Monitoring
Your open source window into the Elastic Stack — query, visualize, and act on data stored in Elasticsearch with real-time dashboards, AI-assisted search, and automated alerting.
Laudspeaker
Marketing · Automation
Open-source customer engagement platform for building visual, event-triggered messaging journeys across email, SMS, push, in-app, and webhooks.
Lightdash
Analytics · Data Engineering
The open-source Looker alternative that turns your dbt project's metrics and dimensions into governed, self-serve charts and dashboards — no license key required.
Mistle
AI Agents · Developer Tools
Self-hostable platform for running autonomous coding agents in isolated, credentialless sandboxes with brokered credentials, reusable snapshots, and event-driven triggers.
Nginx Proxy Manager
Developer Tools · Networking · Security
Manage Nginx reverse proxies and free Let's Encrypt SSL through a beautiful web interface — no Nginx expertise required.
NocoBase
No Code Platforms · Low Code Platforms
Open-source AI + no-code platform that lets coding agents and people collaborate to build business systems fast on proven infrastructure.