doT
A fast, concise JavaScript template engine with zero dependencies, built for high-performance rendering in Node.js and browsers.
Repository Health
Technical Analysis
doT.js is a minimal JavaScript templating engine built purely around performance under V8. Instead of interpreting a template tree at render time, doT compiles templates directly into plain JavaScript functions, which is what gives it its speed advantage over interpreter-style engines. The entire core lives in a single small file with no runtime dependencies, making it easy to vendor or bundle into other tools.
Beyond basic interpolation, doT supports custom delimiters, conditionals, array iteration, partials with parameters, and both compile-time and runtime evaluation. It has historically been popular as an embedded dependency inside other libraries and code-generation tools (notably Ajv, the JSON Schema validator, used doT internally for years) precisely because it is small, dependency-free, and fast enough to use inside hot code paths.
The project also ships a Node-focused mode that auto-compiles .dot, .def, and .jst template files found under a directory, plus a small CLI (dot-packer) for bundling compiled templates into standalone JS files ahead of deployment. The maintainer explicitly recommends precompiling templates at build time rather than compiling them at runtime from untrusted input, since doT allows arbitrary JavaScript inside templates.
What You Get
- A single-file, zero-dependency template compiler usable in Node.js or directly in the browser
- Custom delimiter syntax so templates don’t collide with other templating conventions in the same codebase
- Partials with parameters via
{{#def.name}}for reusable template fragments - Automatic directory-based compilation of
.dot,.def, and.jstfiles viadoT.process() - A
dot-packerCLI for precompiling templates into deployable JS bundles
Common Use Cases
- Embedding a lightweight, fast template compiler inside another library or code-generation tool without adding a dependency tree
- Server-side HTML rendering in Express apps where render speed under load matters
- Precompiling templates to JS at build time for CSP-restricted or performance-sensitive browser apps
- Generating repetitive code or config artifacts from templates as part of a build pipeline
Under The Hood
Architecture
doT’s core (doT.js) is a single self-invoking closure that exposes a template compiler and a shared templateSettings object of regexes controlling delimiter syntax ({{= }}, {{~ }}, {{? }}, {{# }}, etc.). Compilation works by string-substituting each regex match into JS source fragments, then invoking new Function(...) once per template to produce a reusable render function — there is no intermediate AST or virtual DOM, which is the source of both its speed and its notorious unreadability (the maintainer’s own README states it took them years to fully understand the 140-line core). index.js layers a separate InstallDots abstraction on top for directory-based auto-compilation (walking a path, compiling .dot/.def/.jst files, and writing output), which is the only place approaching “application structure” in the package; the rest is a pure library entry point with no framework-like lifecycle.
Tech Stack
The package has zero runtime dependencies, a deliberate design constraint. Dev tooling is a conventional 2016-era Node stack: Mocha for tests, nyc/Istanbul for coverage, ESLint for linting, Travis CI for continuous integration, and commander + mkdirp powering the small dot-packer CLI. There is no build step for the library itself beyond bundling doT.min.js via UglifyJS at publish time (npm run bundle).
Code Quality
Tests exist under test/ (dot.test.js, conditionals.test.js, defines.test.js, iteration.test.js, process.test.js) using Mocha’s BDD describe/it style with Node’s built-in assert, covering template compilation, conditionals, definitions/partials, iteration, and the directory-processing mode, with coverage tracked via nyc. There is no static type system (no TypeScript, no JSDoc type annotations) and no linting/CI activity in recent years — the .travis.yml config is stale and the project has had no commits since late 2023, so quality signals reflect a mature but effectively dormant codebase rather than active engineering discipline.
API Design
The public API is intentionally tiny: doT.template(str, settings, defs) returns a function, and that’s most of what a consumer needs to learn. This minimalism is doT’s defining trade-off — the delimiter-based template syntax is expressive (conditionals, loops, partials, raw JS evaluation) but the underlying regex/string-substitution implementation is dense and hard to extend safely, which is why the maintainer discourages deep customization. Getting started requires almost no boilerplate, but templates that use arbitrary embedded JavaScript push a real security burden onto the consumer, which the README addresses directly and at length.