{"id":13281,"library":"handlebars","title":"Handlebars.js Templating Engine","description":"Handlebars.js is a robust and widely-used JavaScript templating engine that facilitates the creation of semantic templates with minimal frustration. It is largely compatible with Mustache templates, allowing developers to often swap engines without major template modifications. The current stable version, 4.7.9, focuses on ongoing maintenance, including crucial security patches, bug fixes, and type definition enhancements within the 4.x series. Handlebars differentiates itself by providing powerful features such as custom helpers, block expressions, nested path support, and the ability to precompile templates for improved client-side performance. While striving for compatibility with Mustache, it introduces its own extensions and deviates in areas like recursive lookup, which requires an explicit `compat` flag. It is suitable for both browser and Node.js environments and ships with TypeScript type definitions, making it well-suited for modern JavaScript and TypeScript projects.","status":"active","version":"4.7.9","language":"javascript","source_language":"en","source_url":"https://github.com/handlebars-lang/handlebars.js","tags":["javascript","handlebars","mustache","template","html","typescript"],"install":[{"cmd":"npm install handlebars","lang":"bash","label":"npm"},{"cmd":"yarn add handlebars","lang":"bash","label":"yarn"},{"cmd":"pnpm add handlebars","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"Handlebars is typically imported as a default export in ESM.","wrong":"import { Handlebars } from 'handlebars';","symbol":"Handlebars","correct":"import Handlebars from 'handlebars';"},{"note":"For CommonJS environments, the entire Handlebars object is usually imported. The `compile` method is accessed via `Handlebars.compile`.","wrong":"const { compile } = require('handlebars');","symbol":"Handlebars (CommonJS)","correct":"const Handlebars = require('handlebars');"},{"note":"Use `import type` to import only the type definition for `TemplateDelegate` for better bundler tree-shaking and clearer intent in TypeScript.","wrong":"import { TemplateDelegate } from 'handlebars';","symbol":"TemplateDelegate (Type)","correct":"import type { TemplateDelegate } from 'handlebars';"}],"quickstart":{"code":"import Handlebars from 'handlebars';\n\ninterface Kid {\n  name: string;\n  age: string;\n}\n\ninterface Context {\n  name: string;\n  hometown: string;\n  kids: Kid[];\n}\n\nconst source = `<p>Hello, my name is {{name}}. I am from {{hometown}}. I have ` +\n             `{{kids.length}} kids:</p>` +\n             `<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>`;\n\nconst template = Handlebars.compile<Context>(source);\n\nconst data: Context = { \n  \"name\": \"Alan\", \n  \"hometown\": \"Somewhere, TX\",\n  \"kids\": [{\"name\": \"Jimmy\", \"age\": \"12\"}, {\"name\": \"Sally\", \"age\": \"4\"}]\n};\n\nconst result = template(data);\n\nconsole.log(result);\n\n// Registering a custom helper\nHandlebars.registerHelper('loud', function(text) {\n  return text.toUpperCase();\n});\n\nconst helperSource = `<h1>{{loud name}}</h1>`;\nconst helperTemplate = Handlebars.compile<Pick<Context, 'name'>>(helperSource);\nconst helperResult = helperTemplate(data);\nconsole.log(helperResult);\n","lang":"typescript","description":"Demonstrates basic template compilation, data rendering, and the registration and usage of a custom helper with TypeScript types."},"warnings":[{"fix":"Upgrade to Handlebars.js version 4.7.9 or newer immediately. Ensure any user-controlled input passed to `Handlebars.compile()` is always a string and not a parsed object/AST. Consider using the runtime-only build (`handlebars/runtime`) if templates are pre-compiled.","message":"Handlebars.js version 4.7.9 fixes a critical JavaScript Injection vulnerability (GHSA-2w6w-674q-4c4q / CVE-2026-33937). An attacker could achieve Remote Code Execution by supplying a crafted Abstract Syntax Tree (AST) to `Handlebars.compile()` if user-controlled input was not properly sanitized.","severity":"breaking","affected_versions":"<4.7.9"},{"fix":"Update templates to use explicit path references instead of relying on prototype chain traversal. If absolutely necessary, specific properties or methods can be allowed via runtime-options, but this is discouraged.","message":"Access to prototype properties is strictly forbidden by default since version 4.6.0, and more rigorously enforced with `strict: true` in later 4.x versions. This change prevents prototype pollution attacks. Code relying on implicit prototype chain lookups will break.","severity":"breaking","affected_versions":">=4.6.0"},{"fix":"For optimal performance, explicitly reference paths in templates (e.g., `{{../parent.property}}`). If Mustache-style recursive lookup is critical, enable the `compat` option during compilation, being mindful of the performance implications.","message":"Handlebars does not perform recursive lookup by default, a subtle difference from Mustache. Enabling the `compat` compile-time flag restores this behavior but incurs a performance cost.","severity":"gotcha","affected_versions":">=4.0.0"},{"fix":"Ensure you are using at least Handlebars v4.7.8 or newer to benefit from fixes addressing bundler compatibility with ESM imports. Verify your bundler configuration to correctly handle ESM and CommonJS interop.","message":"Older versions of Handlebars (e.g., prior to 4.7.8) had known issues with modern bundlers like Rollup and Webpack 5 when importing as an ESM module.","severity":"gotcha","affected_versions":"<4.7.8"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"In Node.js or modern environments, add `import Handlebars from 'handlebars';` (ESM) or `const Handlebars = require('handlebars');` (CommonJS). In the browser, ensure the Handlebars script is loaded before your application code.","cause":"The Handlebars library was not imported or included correctly in the execution environment (e.g., missing `require` or `import` statement, or not included in a browser script tag).","error":"ReferenceError: Handlebars is not defined"},{"fix":"Register your custom helper function globally using `Handlebars.registerHelper('myCustomHelper', myFunction);` or pass it as an option to the `compile` or `render` method, ensuring it's available in the template's scope.","cause":"A custom helper was used in a template but not registered with `Handlebars.registerHelper()` before template compilation or rendering.","error":"Error: Missing helper: 'myCustomHelper'"},{"fix":"Carefully review the template syntax around the indicated line number. Common issues include unclosed `{{#block}}...{{/block}}`, `{{^invertedBlock}}...{{/invertedBlock}}`, `{{else}}` placement, or incorrect helper arguments.","cause":"There is a syntax error in your Handlebars template (e.g., unclosed block, invalid helper syntax, malformed expression).","error":"Template compilation error: Parse error on line X: Expected EOF, got '{{'"},{"fix":"Ensure that the data object passed to the template function contains all expected properties, or implement checks within the template (e.g., `{{#if someArray.length}}{{/if}}`) to gracefully handle missing or undefined data.","cause":"The template is attempting to access a property (like `length`) on a data variable that is `undefined` or `null` within the provided context.","error":"TypeError: Cannot read properties of undefined (reading 'length')"}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":null,"cli_name":"handlebars","cli_version":null}