Skip to content

Search tags, filters, functions and guides.

Guides / Benchmarks

Guides

Benchmarks

How fast Kiln renders real pages, compared with other JavaScript template engines.

We render the same pages with Kiln and 7 other JavaScript template engines, and time how long each engine takes to render each page. Each page is something a real site renders, from a blog post to an admin table. Together they cover the template features sites use most. We didn't pick them because Kiln does well on them.

Longer bars are faster, and the fastest engine is listed first. The last run was on 17 October 2026, with Node 24.22.0 on Apple M4 Pro. Read how the benchmark works before you compare engines.

Blog post

A full article page: a layout with blocks, header and footer partials, trusted body HTML, related posts and twenty escaped comments.

Uses: Layouts, Partials, Loops, Conditionals, Escaping, Dates

Engine Renders per second
Kiln 1.0.0
113,374
Pug 3.0.4
55,087
Eta 4.6.0
50,198
Handlebars 4.7.9
27,818
Nunjucks 3.2.4
25,768
EJS 6.0.1
20,120
Twing 7.3.1
3,771
LiquidJS 10.29.0
1,281

CMS landing page

A page built from ten content sections, where each section's type picks the partial that renders it.

Uses: Dynamic partials, Nested loops, Conditionals, Prices

Engine Renders per second
Pug 3.0.4
197,876
Kiln 1.0.0
195,451
Eta 4.6.0
166,192
Handlebars 4.7.9
34,054
Nunjucks 3.2.4
25,600
EJS 6.0.1
17,489
Twing 7.3.1
4,105
LiquidJS 10.29.0
3,917

Product grid

A shop category page that renders a product card component 48 times, with sale prices, badges and star ratings.

Uses: Components, Slots, Loops, Prices

Engine Renders per second
Kiln 1.0.0
46,613
Eta 4.6.0
39,960
Pug 3.0.4
26,423
Nunjucks 3.2.4
7,004
Handlebars 4.7.9
6,924
EJS 6.0.1
4,333
Twing 7.3.1
829
LiquidJS 10.29.0
762

Data table

An admin table with 500 rows and 6 columns. Mostly loop and output speed.

Uses: Large loops, Loop variables, Escaping

Engine Renders per second
Kiln 1.0.0
18,554
Eta 4.6.0
10,983
Pug 3.0.4
7,860
Handlebars 4.7.9
2,974
Nunjucks 3.2.4
2,529
EJS 6.0.1
1,714
Twing 7.3.1
247
LiquidJS 10.29.0
220

Search results

25 search results that run text through filters: truncate, join, sort, case changes, defaults, URL encoding and number formatting.

Uses: Filters, Defaults, URL encoding

Engine Renders per second
Kiln 1.0.0
126,765
Eta 4.6.0
74,273
Pug 3.0.4
68,268
Handlebars 4.7.9
35,357
Nunjucks 3.2.4
29,560
EJS 6.0.1
24,444
LiquidJS 10.29.0
3,532
Twing 7.3.1
3,168

Async data

A blog index that loads its own posts, categories and each post's author by calling async functions from the template.

Uses: Async functions, Loops

Engine Renders per second
Kiln 1.0.0
155,177
Eta 4.6.0
117,456
Nunjucks 3.2.4
43,947
EJS 6.0.1
32,250
Twing 7.3.1
2,594
LiquidJS 10.29.0
2,286
Handlebars 4.7.9 Can't wait for async data
Pug 3.0.4 Can't wait for async data

CMS data

A blog post from a headless CMS. The page loads its post and the latest posts, and the layout loads the header and footer menus. Each call waits 2 ms, as a database query would.

Uses: Async functions, Layouts, Partials

Engine Renders per second
Kiln 1.0.0
385
Eta 4.6.0
196
EJS 6.0.1
98
Nunjucks 3.2.4
97
LiquidJS 10.29.0
88
Twing 7.3.1
87
Handlebars 4.7.9 Can't wait for async data
Pug 3.0.4 Can't wait for async data

First render

The blog post page from a cold start: create the environment, compile every template and render once. This is what a serverless cold start or a template edit in a CMS costs.

Uses: Parsing, Compiling

Engine Renders per second
Eta 4.6.0
19,145
Kiln 1.0.0
11,112
EJS 6.0.1
8,453
Nunjucks 3.2.4
3,568
Handlebars 4.7.9
1,557
Twing 7.3.1
1,525
Pug 3.0.4
524
LiquidJS 10.29.0
476

How the benchmark works

  • Same HTML. Before timing anything, every engine renders every page and the benchmark checks the HTML matches Kiln's. Only whitespace and the way quotes are escaped may differ. An engine can't win by doing less work.
  • Idiomatic templates. Each engine's templates use its own way of doing things: its layouts, partials, components or macros, and its built-in filters. Where an engine has no built-in for something, such as formatting a price, it gets a small helper with the same output, as its users would add.
  • Production set-up. Templates are compiled once and cached, output is HTML-escaped, and templates load from memory, so no time goes on reading files. Engines with a synchronous API use it, except in the async data page.
  • Separate processes. Each engine runs in its own Node process, so one engine can't slow another down. Each page is rendered for 250 ms to warm up, then timed for 1,000 ms with tinybench. Time per render is the mean of every timed render. Renders per second is 1,000 divided by that time in milliseconds.
  • Async data. Handlebars and Pug have no way for a template to wait for a promise, so they sit out the async data and CMS data pages. On the async data page, the async functions resolve straight away, so it measures the cost of waiting, not of fetching data.
  • CMS data. Each data function waits 2 ms before it returns, about as long as a query to a database near the server takes. Kiln runs calls that don't depend on each other at the same time, so the page's two documents and the layout's two menus all load at the same time. Eta templates are JavaScript, so its templates start calls together with Promise.all. The other engines wait for each call to finish before they start the next. On a real site, the difference depends on where the data comes from, how long each call takes and how many calls a page makes.
  • Precompiled Kiln. Kiln renders precompiled templates, as its docs recommend for the templates in your codebase. The benchmark precompiles them before timing starts, as a site's build would.
  • First render. This page times creating an environment, compiling every template and rendering once. A serverless cold start, or editing a template in a CMS, costs about this much. Kiln compiles from source here, as it does for a template edited in a CMS.

The templates for every engine are in packages/benchmarks/templates. If you know a faster way to write one of them in its engine, please open a pull request.

Reading the results

  • Numbers depend on the machine. Compare engines within one run, not with numbers from another computer.
  • Nunjucks, Handlebars, EJS, Eta and Pug turn templates into JavaScript and run it with new Function. That makes compiled templates fast. But Cloudflare Workers blocks new Function, so on Workers those engines need a build step that compiles templates ahead of time. Kiln generates JavaScript only when you precompile at build time, so the same templates run on Workers. LiquidJS and Twing don't generate code.
  • Kiln runs with its defaults, including strict mode, which checks every variable and property a template reads.

Run it yourself

From the root of the repository:

npm install
npm run bench

This builds Kiln, checks every engine's output, runs the benchmark and writes packages/benchmarks/results.json, which this page reads. A full run takes about two minutes.

Last runDetails
Date17 October 2026
Kiln commitc5e1a5b
Node24.22.0
CPUApple M4 Pro, 12 cores
Memory24 GB
Operating systemDarwin 25.3.0 (arm64)