I have to fully credit Tal Bereznitskey for his awesome blog post on precompiling Handlebars templates. With a few minor tweaks, it saved me a great deal of time trying to figure out how to precompile a dozen or so templates in a Backbone application I’ve been working on.
If you’re new to Handlebars, you may want to start with this Handlebars tutorial I wrote.
Why Precompile?
If you think of an individual tweet as a Handlebars template, you can understand why it would be expensive to compile a template each time a tweet is rendered. Precompiling templates saves time on the client side by reducing your application’s runtime.
If you’ve looked into precompiling Handlebars templates, you’ve undoubtedly come across the Handlebars precompiler. You don’t actually need to use the precompiler to precompile your templates, so don’t feel confused if you don’t fully understand the documentation and don’t see it referenced here.
Move Templates to a Templates Folder
In a typical Handlebars application, you may include your templates in <script> tags in your HTML files. If you want to precompile your templates, create a templates directory in your application wherever you would normally house your JavaScript files. Inside, create a single file for each template with a .handlebars extension.
So, if you previously included the following in your HTML file:
<script id="image-template" type="text/x-handlebars-template"> <div class="input-container csv"> <p>Image</p> <input class="content csv-image imgUrl" placeholder="Image URL"/></br> <input class="content csv-target csvTargetUrl" placeholder="TargetURL"/></br> <input class="content csv-link csvLinkName" placeholder="Clickthrough Link Name"/> <button class="remove-image button"><span>X</span></button> </div> </script>
You would now create a file called image-template.handlebars inside your templates directory. The code would include the template only, without the need for the <script> tags:
<div class="input-container csv"> <p>Image</p> <input class="content csv-image imgUrl" placeholder="Image URL"/></br> <input class="content csv-target csvTargetUrl" placeholder="TargetURL"/></br> <input class="content csv-link csvLinkName" placeholder="Clickthrough Link Name"/> <button class="remove-image button"><span>X</span></button> </div>
Do this for each of your templates.
Create a getTemplate Function
Create a separate templates.js file inside your JavaScript directory.
// precompiles handlebars templates Handlebars.getTemplate = function(name) { console.log("templates function working"); if (Handlebars.templates === undefined || Handlebars.templates[name] === undefined) { console.log("template undefined"); $.ajax({ url : 'js/templates/' + name + '.handlebars', success : function(data) { console.log("success compiling template"); if (Handlebars.templates === undefined) { Handlebars.templates = {}; } Handlebars.templates[name] = Handlebars.compile(data); }, async : false }); } return Handlebars.templates[name]; };
I’ve added a few console.log statements which will demonstrate whether this function is working properly.
Make sure to include templates.js in your HTML file after Handlebars:
<script src="js/templates.js"></script>
Swap Handlebars.compile for Handlebars.getTemplate
In your application, where you would normally compile the Handlebars template:
var template = Handlebars.compile($("#image-template").html());
You would now call the getTemplate function and pass in the template you’d like to access:
var template = Handlebars.getTemplate("image-template");
Watch Your Console
If you watch your console as you test your application, you’ll see that the first time you call the getTemplate function, your getTemplate function runs, determines that the template is undefined, and compiles the template. On subsequent calls passing in the same template, the function determines that the template is already compiled and does not compile again:
- PM Career Story - April 28, 2022
- How to Transition into Product Management - December 26, 2017
- What I’ve Learned in My First Few Months as a Product Manager - October 14, 2015
Ty says
I think it would help if you added a note that you had retrieve the template using a blocking AJAX call when you did async: false. I had to resort to this myself in some cases for certain calls to the server.
Koren Leslie Cohen says
Thanks for your thoughts!
Tarun Mookhey says
Great post. As an addition, I wrote some code to automate the file creation process. It saved a tonne of time with a Backbone application which contained over 40 templates, check out https://gist.github.com/tarun-pacifica/a7c5c2993d40df4f39691b3cd35be367
Koren Leslie Cohen says
Thanks – will check it out!
Niko says
Great write-up, that said what you are doing is NOT pre-compilation but runtime compilation + caching:
– Assuming you do NOT have a single page application
– You still need to compile templates once for each page load
– This means your user will STILL be hit with the performance penalty EVERY time he hits a specific page since the js state is wiped
– If you use one template many times one the same page before redirecting this can still increase performance
The difference to actual pre-compilation:
– No action needed during run time. ALL your templates are already compiled + cached.
– You can now ship the handlebar runtime only which increases initial load speed of your page since its sign. smaller
What I usually suggest:
– setup a proper “build” pipeline that includes handlebar pre-compilation
– this way pre-compilation happens automatically every time you build your project, hence after setup there is no ongoing work to maintain the compiled templates while still getting all the perf benefits
Here is how you do this with grunt (you can use other build tools as well):
http://danburzo.ro/grunt/chapters/handlebars/
JItender Bhalla says
Excellent Article
Prasanna Poonacha says
Great article Koren 🙂 I loved it.