Intuition

Think of Twig templates and JavaScript as two different worlds that need to communicate. Twig lives on the server side, where it has access to all your application data, database results, and configuration settings. JavaScript, on the other hand, lives in the browser, where it handles user interactions and dynamic updates. And oftentimes to make some interactable components, we’ll need to pass information from one world to another in order to make that happen.

Methods for Passing Variables

Consider we have the following test-hello-world component:

1
2
3
4
test-hello-world
            |______test-hello-world.twig
            |______test-hello-world.js
            |______test-hello-world.component.yml

In the Twig template there is a button that storing some data that needs to be read from the JavaScritp file in order for the button to trigger and alert() when it is clicked.

Method-1: via HTML data attribute

(recommended for SDC, good for simple values such as raw string)

2025-07-22T104255

1
2
3
4
5
6
7
8
{# Method 1a: Passing varaibale via embedding raw value in html data attribute (good for simple values) #}
{% set hello_world_name = 'Simon' %}
<button 
	class="hello-world-button method-1 btn btn-primary" 
	data-hello-world-name="{{hello_world_name}}"
>
	BUTTON (METHOD 1a)
</button>
1
2
3
4
5
6
/* Method 1a: Passing varaibale via embedding raw value in html data attribute (good for simple values)  */
const button_s = document.querySelectorAll('.hello-world-button.method-1');
button_s.forEach((button) => {
    const data     = button.dataset.helloWorldName;    //OR "button.getAttribute('data-hello-world-name');"
    button.onclick = () => {alert("Read Data:\n" + data + "\n(method-1a)");}
});

There’s a workaround for when the data/values are complex objects, shown below. But you’ll need to be mindful of the usage for single/double quote, if not you’ll get parsing error (e.g. <... data-example-data="{"name":"Simon", "gender":"male"}" >). So generally it is just better and more convinient to use the method-2 when the data is not a plain string or number.

1
2
3
4
5
6
7
8
{# Method 1b: Passing variable via embedding encodedJSON object in data attribute (good for complex values) #}
{% set hello_world_names = ['Simon', 'John', 'Jane'] %}
<button 
	class="hello-world-button method-2 btn btn-primary" 
	data-hello-world-names='{{hello_world_names|json_encode|raw}}'
>
	BUTTON (METHOD 1b)
</button>
1
2
3
4
5
6
/* Method 1b: Passing variable via embedding encodedJSON object in data attribute (good for complex values) */
const button_s = document.querySelectorAll('.hello-world-button.method-2');
button_s.forEach((button) => {
    const data     = JSON.parse(button.dataset.helloWorldNames);   //OR "JSON.parse(button.getAttribute('data-hello-world-names'));"
    button.onclick = () => {alert("Read Data:\n" + data + "\n(method-1b)");}
});

Method-2: via Storing Variable to Window

(recommended for SDC, when you have complex data)

2025-07-22T105910

1
2
3
{# Method 2a: Passing variable via storing JSON object in script tag #}
<button class="hello-world-button method-2a btn btn-primary">BUTTON (METHOD 2a)</button>
<script>window.helloWorldNames={{hello_world_names|json_encode|raw}};</script>
1
2
3
4
5
6
/* Method 2a: Passing variable via storing JSON object in script tag */
const button_s = document.querySelectorAll('.hello-world-button.method-2a');
button_s.forEach((button) => {
    const data      = window.helloWorldNames;
    button.onclick = () => {alert("Read Data:\n" + data + "\n(method-2a)");}
});

Sometimes you may have many instances of the same component. In that scenario, as you can’t tweak every single component to have different template, (they share the same template), you will need to use an array or dictionary instead.

Using Array:

1
2
3
4
5
6
7
{# Method 2b: Passing multiple variable via storing JSON object in script tag (via array) #}
{% set hello_world_names = ['Simon', 'John', 'Jane'] %}
<button class="hello-world-button method-2b btn btn-primary">BUTTON (METHOD 2b)</button>
<script>
    window.helloWorldNamesArray = window.helloWorldNamesArray||[];
    window.helloWorldNamesArray.push({{hello_world_names|json_encode|raw}});
</script>
1
2
3
4
5
6
7
/* Method 2b: Passing multiple variable via storing JSON object in script tag (via array) */
const button_s = document.querySelectorAll('.hello-world-button.method-2b');
for (let i = 0; i < button_s.length; i++) {
    const button    = button_s[i];
    const data      = window.helloWorldNamesArray[i];
    button.onclick = () => {alert("Read Data:\n" + data + "\n(method-2b)");}
}

Using Dictionary: (recommended due to its rigidity)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{# Method 3b: Passing multiple variable via storing JSON object in script tag (via dictionary) #}
{% set hello_world_names = ['Simon', 'John', 'Jane'] %}
{% set hello_world_random_id = "hello-world-button-" ~ random() %}
<button 
	class="hello-world-button method-2c btn btn-primary" 
	data-hello-world-id="{{hello_world_random_id}}"
>
	BUTTON (METHOD 2c)
</button>
<script>
    window.helloWorldNamesDict = window.helloWorldNamesDict||{};
    window.helloWorldNamesDict["{{hello_world_random_id}}"] = {{hello_world_names|json_encode|raw}};
</script>
1
2
3
4
5
6
7
/* Method 3b: Passing multiple variable via storing JSON object in script tag (via dictionary) */
const button_s = document.querySelectorAll('.hello-world-button.method-2c');
button_s.forEach((button) => {
    const id        = button.dataset.helloWorldId;
    const data      = window.helloWorldNamesDict[id];
    button.onclick = () => {alert("Read Data:\n" + data + "\n(method-2c)");}
});