Vite support for Handlebars
I really like Vite as a simple static site bundler. It can handle bundling multiple HTML files, which is great, but lacks the ability out-of-the-box to share parts of those HTML files.
While a JS framework like React or Vue could be used to solve this problem, this is heavy-handed for a simple site that could be completely pre-rendered without a JS run-time of any kind.
Handlebars provides what we need to be able to stitch together multiple HTML files, interpolate variables, etc.
Start by installing the package like you would any other
yarn add -D vite-plugin-handlebars
It can then be added to your Vite configuration as a plugin:
// vite.config.js
import handlebars from 'vite-plugin-handlebars';
export default {
plugins: [handlebars()],
};
Configuring the plugin is covered later in this guide.
- This plugin is intended to work with Vite 2
- This plugin requires Node 14 or higher (due to usage of
fs/promises
)
If you want to make use of Handlebars Context to inject variables into your HTML file, you'll need to define their values in the context
object passed to the handlebars
plugin:
<!-- index.html -->
<h1>{{title}}</h1>
// vite.config.js
import handlebars from 'vite-plugin-handlebars';
export default {
plugins: [
handlebars({
context: {
title: 'Hello, world!',
},
}),
],
};
This will result in <h1>Hello, world!</h1>
in your output HTML file.
You can also provide a (asynchronous) function, either as the context
key or any of the keys within the object, which will be evaluated to create the value that will be made available inside your page. This function is called with an identifier parameter based on the HTML file path which makes it possible to provide unique data to each HTML page in a multipage application setup.
// vite.config.js
import handlebars from 'vite-plugin-handlebars';
const pageData = {
'/index.html': {
title: 'Main Page',
},
'/nested/subpage.html': {
title: 'Sub Page',
},
};
export default {
plugins: [
handlebars({
context(pagePath) {
return pageData[pagePath];
},
}),
],
};
If you want to make use of partials in your HTML files, you must define the partialDirectory
option for the handlebars
plugin.
// vite.config.js
import { resolve } from 'path';
import handlebars from 'vite-plugin-handlebars';
export default {
plugins: [
handlebars({
partialDirectory: resolve(__dirname, 'partials'),
}),
],
};
If you want to use multiple partial folders, an array can be submitted.
Each file in these directories (.html
or .hbs
) will become registered as a partial. The name of the file is used to invoke it. So, with the above configuration and the following files:
<!-- partials/header.hbs -->
<header><a href="/">My Website</a></header>
<!-- index.html -->
{{> header }}
<h1>The Main Page</h1>
Your output website content would become:
<header><a href="/">My Website</a></header>
<h1>The Main Page</h1>
Make sure to review the quirks section for information on potentially-unexpected behavior.
Custom helpers can be registered using the helpers
configuration option:
// vite.config.js
import { resolve } from 'path';
import handlebars from 'vite-plugin-handlebars';
export default {
plugins: [
handlebars({
helpers: {
capitalize: (value) => value.toUpperCase(),
},
}),
],
};
For more information on helpers, see the Handlebars documentation.
All other Handlebars configuration options can also be passed through.
compileOptions
can be used to alter the compilation stepruntimeOptions
can be used to alter the rendering step
Each of these can also be passed through to the handlebars
plugin:
// vite.config.js
import handlebars from 'vite-plugin-handlebars';
export default {
plugins: [
handlebars({
compileOptions: {
// Example config option: avoid auto-indenting partials
preventIndent: true,
},
runtimeOptions: {
// Example config option: define custom private @variables
data: {
foo: 'bar',
},
},
}),
],
};
By default, any time a partial changes, your browser window will be full reloaded. If you want to disable this behavior, you can set reloadOnPartialChange
to false
:
// vite.config.js
import handlebars from 'vite-plugin-handlebars';
export default {
plugins: [
handlebars({
reloadOnPartialChange: false,
}),
],
};
You can resolve a file path relative to the Vite root using the resolve-from-root
helper. This assists with injecting other files, like linking to a CSS file, within a partial.
- Assets included in a partial using a relative path will probably not work how you would first expect; the relative path is left alone, making it relative to the output file, not the partial itself. It's recommended that you use the
resolve-from-root
helper to ensure paths are resolved from the project root, rather than relative to a particular file.