bonsai-js vs Jexl: Which JavaScript Expression Evaluator Should You Choose?
I needed a JavaScript expression evaluator for a business rules engine. The expressions would be evaluated frequently in production, so performance mattered. I also wanted modern JavaScript syntax - optional chaining, nullish coalescing, template literals - without the complexity of sandboxed JS environments like quickjs-wasm.
The Problem
My application needed to evaluate user-defined expressions safely. Business rules, template systems, conditional logic - all requiring expression evaluation. I looked at Jexl first since it’s established and widely used.
But I hit a wall. Jexl doesn’t support modern JavaScript syntax. No optional chaining (?.), no nullish coalescing (??), no template literals. I found myself writing verbose fallback expressions:
// What I wanted to writeuser?.profile?.name ?? "Anonymous"
// What I had to write with Jexluser.profile && user.profile.name ? user.profile.name : "Anonymous"I even considered extending Jexl’s language to support these features. Then I discovered bonsai-js.
What I Found
bonsai-js is a newer expression evaluator built with modern JavaScript syntax in mind. Here’s what caught my attention:
Modern syntax support out of the box:
import { evaluate } from 'bonsai';
const context = { user: { profile: { name: 'Alice' } } };
// Optional chainingconst name = await evaluate('user?.profile?.name', context);// Result: 'Alice'
// Nullish coalescingconst displayName = await evaluate('user?.profile?.nickname ?? "Guest"', context);// Result: 'Guest'
// Template literalsconst greeting = await evaluate('`Hello, ${user.profile.name}!`', context);// Result: 'Hello, Alice!'
// Spread and lambdasconst doubled = await evaluate('[1, 2, 3].map(x => x * 2)', {});// Result: [2, 4, 6]Performance that matters:
import { compile } from 'bonsai';
const compiled = compile('user?.permissions?.includes("admin") ?? false');const context = { user: { permissions: ['admin', 'user'] } };
// 30M ops/sec on cached expressionsfor (let i = 0; i < 1_000_000; i++) { compiled(context);}The 30M ops/sec comes from a Pratt parser with compiler optimizations - constant folding, dead branch elimination - and LRU caching for repeated expressions.
Zero dependencies:
bonsai: 0 dependenciesjexl: multiple dependenciesSmaller bundle, fewer security concerns, simpler dependency tree.
Jexl’s Strengths
Jexl isn’t without merit. It has a mature ecosystem with real tooling:
- Syntax highlighting extensions for editors
- Linting support
- Custom transformer system for domain-specific operations
import Jexl from 'jexl';
// Jexl's transformer systemJexl.addTransform('upper', val => val.toUpperCase());const result = await Jexl.eval('user.name|upper', context);// Result: 'ALICE'If you’ve invested in Jexl-specific tooling, the ecosystem advantage is real.
Feature Comparison
| Feature | bonsai-js | Jexl |
|---|---|---|
Optional chaining (?.) | Yes | No |
Nullish coalescing (??) | Yes | No |
| Template literals | Yes | No |
| Spread operator | Yes | No |
| Lambda expressions | Yes | Limited |
| Zero dependencies | Yes | No |
| Performance (cached) | 30M ops/sec | Lower |
| TypeScript built-in | Yes | Via @types |
| Editor extensions | Coming | Yes |
| Custom transformers | No | Yes |
| Maturity | New | Established |
The Decision
For my new project, I chose bonsai-js. Here’s why:
- Modern syntax reduces cognitive load - I write expressions the same way I write JavaScript
- Performance isn’t a bottleneck - 30M ops/sec means expression evaluation won’t slow down my app
- Zero dependencies - Cleaner bundle, fewer security concerns
- Built-in TypeScript - No separate @types package needed
For validation and tooling, bonsai’s validate() function extracts references from expressions:
import { validate } from 'bonsai';
const result = validate('user.profile.age > 18');console.log(result.references);// ['user', 'profile', 'age']This enables editor integrations similar to what Jexl offers.
When to Stick with Jexl
If you have an existing Jexl codebase, consider:
- Do you use custom transformers heavily?
- Have you built editor extensions around Jexl?
- Is your team familiar with Jexl’s syntax?
If yes to any of these, migration cost may outweigh immediate benefits. But if you’re hitting syntax limitations frequently, the switch could be worth it.
Common Mistakes to Avoid
- Choosing based solely on npm downloads - Jexl has more downloads, but bonsai may be better for your use case
- Not considering syntax migration cost - Map your expressions before switching
- Assuming quickjs-wasm is the only safe option - Both libraries provide sandboxed evaluation
- Overlooking performance impact - Expression evaluation can become a bottleneck in hot paths
Summary
In this post, I compared bonsai-js and Jexl for JavaScript expression evaluation. The key points:
- bonsai-js for new projects: modern syntax, 30M ops/sec performance, zero dependencies
- Jexl for existing codebases: mature tooling, custom transformers, established ecosystem
- Both provide safe sandboxed evaluation without quickjs-wasm complexity
For my business rules engine, bonsai-js won because modern syntax and performance mattered more than mature tooling. Your mileage may vary.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments