MCPcopy Index your code
hub / github.com/ericf/express-handlebars

github.com/ericf/express-handlebars @v3.1.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v3.1.0 ↗ · + Follow
6 symbols 8 edges 7 files 0 documented · 0% 6 cross-repo links
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Express Handlebars

Join the chat at https://gitter.im/ericf/express-handlebars

A Handlebars view engine for Express which doesn't suck.

npm version dependency status

This package used to be named express3-handlebars. The previous express-handlebars package by @jneen can be found here.

Goals & Design

I created this project out of frustration with the existing Handlebars view engines for Express. As of version 3.x, Express got out of the business of being a generic view engine — this was a great decision — leaving developers to implement the concepts of layouts, partials, and doing file I/O for their template engines of choice.

Goals and Features

After building a half-dozen Express apps, I developed requirements and opinions about what a Handlebars view engine should provide and how it should be implemented. The following is that list:

  • Add back the concept of "layout", which was removed in Express 3.x.

  • Add back the concept of "partials" via Handlebars' partials mechanism.

  • Support a directory of partials; e.g., {{> foo/bar}} which exists on the file system at views/partials/foo/bar.handlebars, by default.

  • Smart file system I/O and template caching. When in development, templates are always loaded from disk. In production, raw files and compiled templates are cached, including partials.

  • All async and non-blocking. File system I/O is slow and servers should not be blocked from handling requests while reading from disk. I/O queuing is used to avoid doing unnecessary work.

  • Ability to easily precompile templates and partials for use on the client, enabling template sharing and reuse.

  • Ability to use a different Handlebars module/implementation other than the Handlebars npm package.

Package Design

This package was designed to work great for both the simple and complex use cases. I intentionally made sure the full implementation is exposed and is easily overridable.

The package exports a function which can be invoked with no arguments or with a config object and it will return a function (closed over sensible defaults) which can be registered with an Express app. It's an engine factory function.

This exported engine factory has two properties which expose the underlying implementation:

  • ExpressHandlebars(): The constructor function which holds the internal implementation on its prototype. This produces instance objects which store their configuration, compiled and precompiled templates, and expose an engine() function which can be registered with an Express app.

  • create(): A convenience factory function for creating ExpressHandlebars instances.

An instance-based approach is used so that multiple ExpressHandlebars instances can be created with their own configuration, templates, partials, and helpers.

Installation

Install using npm:

$ npm install express-handlebars

Usage

This view engine uses sensible defaults that leverage the "Express-way" of structuring an app's views. This makes it trivial to use in basic apps:

Basic Usage

Directory Structure:

.
├── app.js
└── views
    ├── home.handlebars
    └── layouts
        └── main.handlebars

2 directories, 3 files

app.js:

Creates a super simple Express app which shows the basic way to register a Handlebars view engine using this package.

var express = require('express');
var exphbs  = require('express-handlebars');

var app = express();

app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');

app.get('/', function (req, res) {
    res.render('home');
});

app.listen(3000);

views/layouts/main.handlebars:

The main layout is the HTML page wrapper which can be reused for the different views of the app. {{{body}}} is used as a placeholder for where the main content should be rendered.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Example App</title>
</head>
<body>

    {{{body}}}

</body>
</html>

views/home.handlebars:

The content for the app's home view which will be rendered into the layout's {{{body}}}.

<h1>Example App: Home</h1>

Running the Example

The above example is bundled in this package's examples directory, where it can be run by:

$ cd examples/basic/
$ npm install
$ npm start

Using Instances

Another way to use this view engine is to create an instance(s) of ExpressHandlebars, allowing access to the full API:

var express = require('express');
var exphbs  = require('express-handlebars');

var app = express();
var hbs = exphbs.create({ /* config */ });

// Register `hbs.engine` with the Express app.
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

// ...still have a reference to `hbs`, on which methods like `loadPartials()`
// can be called.

Note: The [Advanced Usage][] example demonstrates how ExpressHandlebars instances can be leveraged.

Template Caching

This view engine uses a smart template caching strategy. In development, templates will always be loaded from disk, i.e., no caching. In production, raw files and compiled Handlebars templates are aggressively cached.

The easiest way to control template/view caching is through Express' view cache setting:

app.enable('view cache');

Express enables this setting by default when in production mode, i.e.:

process.env.NODE_ENV === "production"

Note: All of the public API methods accept options.cache, which gives control over caching when calling these methods directly.

Layouts

A layout is simply a Handlebars template with a {{{body}}} placeholder. Usually it will be an HTML page wrapper into which views will be rendered.

This view engine adds back the concept of "layout", which was removed in Express 3.x. It can be configured with a path to the layouts directory, by default it's set to relative to express settings.view + layouts/

There are two ways to set a default layout: configuring the view engine's defaultLayout property, or setting Express locals app.locals.layout.

The layout into which a view should be rendered can be overridden per-request by assigning a different value to the layout request local. The following will render the "home" view with no layout:

app.get('/', function (req, res, next) {
    res.render('home', {layout: false});
});

Helpers

Helper functions, or "helpers" are functions that can be registered with Handlebars and can be called within a template. Helpers can be used for transforming output, iterating over data, etc. To keep with the spirit of logic-less templates, helpers are the place where logic should be defined.

Handlebars ships with some built-in helpers, such as: with, if, each, etc. Most application will need to extend this set of helpers to include app-specific logic and transformations. Beyond defining global helpers on Handlebars, this view engine supports ExpressHandlebars instance-level helpers via the helpers configuration property, and render-level helpers via options.helpers when calling the render() and renderView() methods.

The following example shows helpers being specified at each level:

app.js:

Creates a super simple Express app which shows the basic way to register ExpressHandlebars instance-level helpers, and override one at the render-level.

var express = require('express');
var exphbs  = require('express-handlebars');

var app = express();

var hbs = exphbs.create({
    // Specify helpers which are only registered on this instance.
    helpers: {
        foo: function () { return 'FOO!'; },
        bar: function () { return 'BAR!'; }
    }
});

app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

app.get('/', function (req, res, next) {
    res.render('home', {
        showTitle: true,

        // Override `foo` helper only for this rendering.
        helpers: {
            foo: function () { return 'foo.'; }
        }
    });
});

app.listen(3000);

views/home.handlebars:

The app's home view which uses helper functions to help render the contents.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Example App - Home</title>
</head>
<body>


  {{#if showTitle}}
    <h1>Home</h1>
  {{/if}}




{{foo}}






{{bar}}



</body>
</html>

More on Helpers

Refer to the Handlebars website for more information on defining helpers:

Metadata

Handlebars has a data channel feature that propagates data through all scopes, including helpers and partials. Values in the data channel can be accessed via the {{@variable}} syntax. Express Handlebars provides metadata about a template it renders on a {{@exphbs}} object allowing access to things like the view name passed to res.render() via {{@exphbs.view}}.

The following is the list of metadata that's accessible on the {{@exphbs}} data object:

  • cache: Boolean whether or not the template is cached.
  • view: String name of the view passed to res.render().
  • layout: String name of the layout view.
  • data: Original data object passed when rendering the template.
  • helpers: Collection of helpers used when rendering the template.
  • partials: Collection of partials used when rendering the template.

API

Configuration and Defaults

There are two main ways to use this package: via its engine factory function, or creating ExpressHandlebars instances; both use the same configuration properties and defaults.

var exphbs = require('express-handlebars');

// Using the engine factory:
exphbs({ /* config */ });

// Create an instance:
exphbs.create({ /* config */ });

The following is the list of configuration properties and their default values (if any):

handlebars=require('handlebars')

The Handlebars module/implementation. This allows for the ExpressHandlebars instance to use a different Handlebars module/implementation than that provided by the Handlebars npm package.

extname=".handlebars"

The string name of the file extension used by the templates. This value should correspond with the extname under which this view engine is registered with Express when calling app.engine().

The following example sets up an Express app to use .hbs as the file extension for views:

var express = require('express');
var exphbs  = require('express-handlebars');

var app = express();

app.engine('.hbs', exphbs({extname: '.hbs'}));
app.set('view engine', '.hbs');

Note: Setting the app's "view engine" setting will make that value the default file extension used for looking up views.

layoutsDir

Default layouts directory is relative to express settings.view + layouts/ The string path to the directory where the layout templates reside.

Note: If you configure Express to look for views in a custom location (e.g., app.set('views', 'some/path/')), and if your partialsDir is not relative to express settings.view + layouts/, you will need to reflect that by passing an updated path as the layoutsDir property in your configuration.

partialsDir

Default partials directory is relative to express settings.view + partials/ The string path to the directory where the partials templates reside or object with the following properties:

  • dir: The string path to the directory where the partials templates reside.
  • namespace: Optional string namespace to prefix the partial names.
  • templates: Optional collection (or promise of a collection) of templates in the form: {filename: template}.

Note: If you configure Express to look for views in a custom location (e.g., app.set('views', 'some/path/')), and if your partialsDir is not relative to express settings.view + partials/, you will need to reflect that by passing an updated path as the partialsDir property in your configuration.

Note: Multiple partials dirs can be used by making partialsDir an array of strings, and/or config objects as described above. The namespacing feature is useful if multiple partials dirs are used and their file paths might clash.

defaultLayout

The string name or path of a template in the layoutsDir to use as the default layout. main is used as the default. This is overridden by a layout specified in the app or response locals. Note: A falsy value will r

Core symbols most depended-on inside this repo

exphbs
called by 1
index.js
create
called by 1
index.js
ExpressHandlebars
called by 0
lib/express-handlebars.js
passError
called by 0
lib/utils.js
passValue
called by 0
lib/utils.js
exposeTemplates
called by 0
examples/advanced/server.js

Shape

Function 6

Languages

TypeScript100%

Modules by API surface

lib/utils.js2 symbols
index.js2 symbols
lib/express-handlebars.js1 symbols
examples/advanced/server.js1 symbols

For agents

$ claude mcp add express-handlebars \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact