{{craftSnippets}}
Home Articles Plugins Starter theme Components on Github Gists About
  • Home
  • Articles
  • Using Javascript in Twig templates with Craft CMS
Posted on Oct 13, 2019 by Piotr Pogorzelski

Using Javascript in Twig templates with Craft CMS

Macro for passing Twig variables into Javascript on github gists.
Learn how to use Javascript in Twig templates in order to make your code more portable and maintainable.
Table of contents:
  • Single file components in Twig
  • js Twig tag
  • Passing data from Twig to JS
  • Javascript helpers plugin
  • Component namespacing

Single file components in Twig #

Why would we want to use Javascript code in templates, alongside HTML - instead of storing it in separate files? What about layer separation? Vue JS framework popularized concept of single file components - code living alongside javascript (and CSS) in one file.

Let's think of the image gallery component. Such component contains HTML markup, some Twig logic, like for loop and image transforms. Then, there is a Javascript function call which is responsible for lightbox. If we store all of that in one file we gain some advantages:

  • Twig and JS code is more tightly knit. You can easily transfer Twig variables into JS.
  • Your code is more portable - you can easily reuse component by just copying a single file to other project.
  • When you want to tweak something, you don't need to look for code in two separate places - Javascript files and Twig template - but only at one. Thanks to that, code is easier to maintain.

There are also disadvantages - the main one is that using Javascript directly in Twig takes away your ability to use preprocessors like Babel. This means that if you want to support legacy browsers, you will not be able to use ES6.

js Twig tag #

js Twig tag is essential to using JS in Twig. It takes JS code passed to it and appends it at end of the template, right before ending body tag. Multiple js tags can be used - content from each of them will be concated with the rest. You don't need to wrap code passed to js into <script> tags - js will do that for you.

js tag has one limitation - it cannot be used inside {% cache %} tags. Caching specific parts of template saves HTML content generated by them to database - so Twig code does not need to run. This however stops js tag from working.

Your Twig component doesn't need to rely only on inline code - if you want to include external JS file from within Twig, you can use registerJsFile function. Link to this file will also be included at the end of the template.

{% do view.registerJsFile('directory/some-file.js') %}

There is also CSS equivalent of js - css tag. It works pretty much the same as js, except it puts its contents into <head> section of the website. I don't really use it - there would be problems with CSS preprocessors like gulp.

Passing data from Twig to JS #

Now that your JS code lives alongside Twig, you can easily transfer Twig variables into Javascript. For example:

alert({{twig_variable|json_encode}});

From Twig point of view, JS code is just text string - that's why we can just concate Twig variables with text that make up JS code. json_encode is used to transform Twig variables into a form that can be consumed by JS. Thanks to that, Twig true will turn into JS true, not 1 - which would happen if we just outputted {{true}} into the template.

Such approach will work, but it's pretty messy to mix Twig and JS like this - it can be hard to recognize on first glance which parts of code is Twig and which is JS. It is better to define JS variables containing data from Twig before JS code starts. You can do that using Twig macro:

{% macro jsVar(variable, jsVariableName) %}
var {{jsVariableName}} = {{variable|json_encode|raw}};
{% endmacro %}

This macro takes two arguments:

  • variable - twig variable or object that needs to be transferred to JS.
  • jsVariableName - JS variable name that will have assigned Twig variable value.

With jsVar macro, our example with alert will look like this:

{{_self.jsVar(twig_variable, 'javascript_variable_name')}}
alert(javascript_variable_name);

Which will render this code - assuming that twig_variable contains string "abc":

var javascript_variable_name = 'abc';
alert(javascript_variable_name);

Javascript helpers plugin #

Javascript helpers is small plugin that I created to make working with JS in Twig easier. It's main feature is transfering all static message translations into javascript array. Normally, you would need to define each message manually, like this:

var message_1 = {{'message_1'|t|json_encode}};
var message_2 = {{'message_2'|t|json_encode}};

With Javascript helpers plugin you can transfer all static message translations at once:

{{craft.jsHelpers.outputMessages('all_messages')}}

Parameter passed to function is name of array. Assuming that you have only two static message translations, rendered result will look like this:

var all_messages = {message_1: 'something 1', message_2: 'something 2'};

You can also use plugin to transfer Twig variables into Javascript by using Twig filter instead of macro:

{{some_twig_variable|jsVar('js_variable_name')}}

Component namespacing #

Some components containing JS code can be used in multiple parts of the website at the same time. Let's think on carousel component. It renders slides using for loop. Carousel script uses carouselSpeed setting which takes value from Twig carousel.speed variable.

{% is carousel is defined %}
	<ul class="js-carousel">
	{% for slide in carousel.slides %}
	<li class="single-slide">
		<img src="{{slide.url}}" alt="">
	</li>
	{% endfor %}
	</ul>

	{% js %}
	{{_self.jsVar(carousel.speed, 'speed')}}
	$('.js-carousel').initialiseCarousel({
		carouselSpeed: speed
	})
	{% endjs %}
{% endif %}

As you can see, we have initialized carousel script on the element with js-carousel class. What if the same carousel component is used simultaneously somewhere else on a currently displayed page? It might display different images, have different carousel.speed setting, but its class attribute stays identical. This means that initializing script with specific settings would affect two carousels at once.

To avoid that, elements in Twig components affected by Javascript should have their classes (or other attributes used by JS) namespaced. Let's add namespace variable to carousel example:

{% is carousel is defined %}
	<ul class="js-carousel{{namespace ?? null}}">
	{% for slide in carousel.slides %}
	<li class="single-slide">
		<img src="{{slide.url}}" alt="">
	</li>
	{% endfor %}
	</ul>

	{% js %}
	{{_self.jsVar(namespace ?? null, 'carousel_namespace')}}
	{{_self.jsVar(carousel.speed, 'speed')}}
	$('.js-carousel'+carousel_namespace).initialiseCarousel({
		carouselSpeed: speed
	})
	{% endjs %}
{% endif %}

Namespace should be passed into the component using with keyword. You should also use only keyword, to ensure that component is isolated from the variable context of its parent file - which might also has its own namespace.

{% include 'carousel_component' with {namespace: '-some-carousel'} only %}

If you use your component in for loop, you can use loop.index or element id as a part of namespace variable:

{% for block in entry.matrixField %}
	{% include 'carousel_component' with {namespace: '-some-carousel-'~block.id, block: block} only %}
{% endfor %}

What about js- prefix that carousel class has? Using that prefix reminds us that this class is used as a javascript hook and NOT for styling. Thanks to that, styling and javascript functionality are decoupled and changing javascript related classes wont break any styling. It's really good convention to follow.


TAGS:
#twig #tutorial
If you want to get latest updates on Craft CMS tutorials and components, follow me on Twitter or subscribe to RSS feed.
Quick links for this article:
Macro for passing Twig variables into Javascript on github gists.
Articles on blog:
  • Frontend testing for Craft CMS websites with Codeception and Cypress
  • Building reactive Craft Commerce product page with Sprig plugin
  • Dynamically generated PDF attachments for Freeform submissions
  • Using template hooks in Craft CMS
  • Alpine JS modal component for Craft CMS
  • Using template UI elements to extend Craft CMS control panel
  • Matrix within a Matrix - possible solutions for Craft CMS
  • Universal email template for Craft CMS
  • Creating attributes table from entry fields in Craft CMS
  • Namespacing forms in Craft CMS
  • Creating map-based navigation for Craft CMS
  • Placeholder image macro for Craft CMS
  • Building AJAX contact form with Craft CMS
  • Using incognito field plugin for Craft CMS
  • Email footer creator made with Craft CMS
  • Infinite scrolling and lazy loading with Craft CMS
  • Using Javascript in Twig templates with Craft CMS
  • Twig templating tips and tricks for Craft CMS
  • Basic SEO functionality for Craft CMS
  • Working with dates in Craft CMS templates
  • Working with SVG images in Craft CMS templates
  • Responsive and lazy-loaded youtube videos with Craft CMS
  • Debugging and inspecting Twig templates in Craft CMS
  • Creating article excerpts with Twig component in Craft CMS
  • Adding favicons to Craft CMS website
  • Truncating text with Twig macros in Craft CMS
  • Universal language switcher for Craft CMS
  • Read time macro for Craft CMS
  • Using attr() function to render HTML attributes in Craft CMS
  • Building dynamic, AJAX based pagination for Craft CMS
  • How to add Disqus comments to Craft CMS website
  • Ellipsis pagination component for Craft CMS
  • Converting email addresses into links using Twig macro
  • Breadcrumb created from URL for Craft CMS
  • Best developer-oriented Craft CMS plugins
  • Search autocomplete component for Craft CMS
  • RSS feed - template component for Craft CMS
  • Testing emails sent by Craft CMS using Mailtrap
  • Quick edit link - Twig component for Craft CMS
  • Filtering entries in control panel using Searchit plugin
  • Fetching routes into Twig templates in Craft CMS


Frontend testing for Craft CMS websites with Codeception and Cypress

Building reactive Craft Commerce product page with Sprig plugin

Dynamically generated PDF attachments for Freeform submissions

Using template hooks in Craft CMS

Alpine JS modal component for Craft CMS

Using template UI elements to extend Craft CMS control panel

Matrix within a Matrix - possible solutions for Craft CMS

Universal email template for Craft CMS

Copyright ©2023 Piotr Pogorzelski