How to change default layout in express using handlebars?

60,997

Solution 1

You can specify what layout you want to use as part of the render call. If you create a new layout called other.hbs, you can then do something like:

res.render('view', { title: 'my other page', layout: 'other' });

To override this for the entire application, you can use:

app.set('view options', { layout: 'other' });

Solution 2

From the handlebars readme:

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});
});

In case you want to set the default layout just for a specific subroute, you might wanna use the following in the top section of your route:

router.all('/*', function (req, res, next) {
    req.app.locals.layout = 'admin'; // set your layout here
    next(); // pass control to the next handler
    });

You can also set the default layout on initialization:

// Create `ExpressHandlebars` instance with a default layout.
var hbs = exphbs.create({
    defaultLayout: 'main',
    helpers      : helpers,

    // Uses multiple partials dirs, templates in "shared/templates/" are shared
    // with the client-side of the app (see below).
    partialsDir: [
        'shared/templates/',
        'views/partials/'
        ]
    });

// Register `hbs` as our view engine using its bound `engine()` function.
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

Solution 3

This should work now..

npm install express-handlebars

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

2 directories, 3 files

app.js

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

var app = express();

app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');

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

app.listen(3000);

views/layouts/main.handlebars:

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

    {{{body}}}

</body>
</html>

Solution 4

I hope you are using express-handlebars. This instructions are for express-handlebars. For hbs, procedures are little different.

Step-1: Require handlebars

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

step-2: Register handlebars template engine. while registering, you can configure

for changing layout directory

const layoutPath = path.join(__dirname, './templates/layouts'); //you can build your desired path
app.engine('handlebars', exphbs({ layoutsDir: layoutPath }));

other available options along with layoutsDir are

interface ExphbsOptions {
    handlebars?: any;
    extname?: string;
    layoutsDir?: string;
    partialsDir?: any;
    defaultLayout?: string;
    helpers?: any;
    compilerOptions?: any;
}

step-3: If you want to change views directory

const viewPath = path.join(__dirname, './templates/views');
app.set('views', viewPath);

step-4: For some template, If you don't wish to give layout, you have to specify as layout: false . Otherwise app will crash. You can configure as follow, if you need.

app.get('/', (req, res, next) => {
    res.render('shop', { title: 'My Shop', layout: false })
});

For more info about express-handlebars

Solution 5

If you're using the 'express-handlebars' module, then the following should work:

// ...
app.set("views", __dirname );

exphbs.ExpressHandlebars.prototype.layoutsDir = 'path/to/directory/';
app.engine('handlebars', exphbs({defaultView: 'name-of-template'}));

// ...

I came to this by digging around in the module's source, it turns out that this line...

// express-handlebars/lib/express-handlebars.js (line 55 in v1.2.2)
ExpressHandlebars.prototype.layoutsDir  = 'views/layouts/';

...is what gives the default behaviour of always looking in '{{whatever you specified}}/views/layouts/'

So essentially - if, perhaps, you have a different dir structure in mind or have some other reason to override it, you can by using the line in my example. Just be sure that you do this before you instantiate exphbs.

If you're using some other module (I'm not sure which are out there) it's likely that they have some similar setting that can be overridden with a bit of jiggery-pokery (just run a 'find' on the file contents for 'views/layouts/'.

note that I am leaving 'app.set("views", __dirname );' as is so that I keep templates anywhere in my server directory and render them like so:

res.render("moduleName/templateName");

After updating to v2.0.1 The above won't work, instead you can pass the default directory in as an argument as below:

var hbs = exphbs.create({
  layoutsDir: 'app/server/',
  ...
Share:
60,997
Alexander Kim
Author by

Alexander Kim

Middle Frontend-engineer. Working with JS: Vue/Nativescript/Electron/Express || Koa.

Updated on July 05, 2022

Comments

  • Alexander Kim
    Alexander Kim almost 2 years

    I am using Express 4.9.0 and express-generator.

    Created boilerplate with a following command:

    express --hbs projectname
    

    Builtin handlebars is using views/layout.hbs by default as a master page. But i cannot see any settings in my app.js to change that behaviour.

    piece of code from my app.js:

    // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'hbs');

    1. How can i change my default layout globally?
    2. What if i want to have 2 or 3 different global layouts?
  • Alexander Kim
    Alexander Kim over 9 years
    Thanks, but can't i do that globally?
  • Dan
    Dan over 9 years
    @Heihachi I have updated the answer to include a global option.
  • Alexander Kim
    Alexander Kim over 9 years
    that's it! If i want few more global layouts i'll just duplicate a line with app.set(), right?
  • Dan
    Dan over 9 years
    How are you going to have multiple global layouts? How will your views know which global layout to use?
  • shriek
    shriek over 9 years
    Late to the party but is it something that comes default in express or is this a hbs thing? Because, I don't see any reference to layout.hbs either in app.js or in routers/index.js.
  • shriek
    shriek over 9 years
    Nevermind, I found it. It's a hbs thing. If not specified it automatically sets the layout to layout. On line 115:- var layout_filename = path.join(view_dir, layout || 'layout');
  • Bharat
    Bharat over 8 years
    @TimothyStrimple just wondering, what if the data i want to send to the view includes the property called layout and i want to use res.render() to inherit a specific layout. multiple global layouts could be handy in a case where i want different assets and headers/footers/sidebars in different sections of the site. i could send JSON data to the layout view to do that, but then i wont be sending pure data to the view.
  • Tyguy7
    Tyguy7 over 7 years
    I had to google for a long time to find this answer. Thanks, wish it was more clear in the docs.
  • ibamboo
    ibamboo over 7 years
    thanks, it works.but, i got another question, how did u know to set "view options", i cant find the reference in expressjs website.
  • chikitin
    chikitin over 5 years
    When I try this i get: ReferenceError: exphbs is not defined
  • venkat7668
    venkat7668 over 5 years
    You might miss this line in your code.. var exphbs = require('express-handlebars');
  • chikitin
    chikitin over 5 years
    Yes. I thought installing it using express install ... would import it automatically. Thank you.
  • reggie
    reggie over 5 years
    Horrible design choice to mix representational variables like that with the other variables...
  • 0xAnon
    0xAnon about 5 years
    app.engine('hbs', hbs.engine); app.set('view engine', 'hbs'); on doing this in the exact same line , my page says: Error: ENOENT: no such file or directory, open '/home/ray/Desktop/phoneSkill/functions/views/layouts/main.h‌​andlebars' I wonder why it is happening ? But when i set var hbs = exphbs.create({ defaultLayout: 'main.hbs', ... in the exact same line it works and show me the pages. I wonder why this is happening ? shouldnot it be able to parse main.hbs automatically. ?
  • skg
    skg about 4 years
    @Coderboi set extname: 'hbs' in config to modify default extension name. Example: var hbs = exphbs.create({ defaultLayout: 'main', extname: 'hbs', ...
  • Allen
    Allen about 4 years
    this is actually deprecated from express 4, it's just a handlebars thing.
  • fdrv
    fdrv over 2 years
    doesnt work for me in 22