Passing CMS data into the <script> portion of a template

Hi

I have a custom html component sitting inside a cms item element. The custom component’s template contains html and a javascript function (in a script tag). The page is .php not .html

It all works well, but would be even better if I could pass a value from my cms source.md into the template.

I had a good chat with ElementsBot and did some cms binding, but, no matter how I try it, interpolation doesn’t work within the script portion of my template. (I have the same variable - {{boundDataFile}} (see below) - in the html part of the template, and the value comes through nicely.

ElementsBot did say that this doesn’t work out of the box, but I think I have followed the suggestions

Here is a summary of what I have:

In the properties file of the custom component:

{
“title”: “CMS Binding”,
“id”: “boundDataFile”,
“text”: {
“default” : “{{item.dataFile}}”
}
}

and in my .md source file:

dataFile: qd_002_02.json

I have entered {{item.dataFile}} into the ‘CMS Binding’ text field in the UI

and in the script portion of my template file I have:

const res = await fetch(‘{{assetPath}}/quiz_data/{{boundDataFile}}’);

In the source of the published page I see:

const res = await fetch(‘../../../resources/quiz_data/{{item.dataFile}}’);

Which looks as though elements is not sending the value through, just the text entered in the properties, i.e. {{{{item.dataFile}}

When {{boundDataFile}} is placed in the html, it passes the value (‘qd_002_02.json’) as expected.

Is there anything else I can try?

You might have try passing the value through the HOOKS.JS file in your component, that is the normal way to make values like this available in your HTML or script.

Thank you for the suggestion! I hadn’t even thought about using the HOOKS.JS.

Unfortunately it doesn’t take me any further in this case. I suspect I am hitting sandboxing or scoping.

My limited understanding of js is very probably an issue as well, ChatGPT can only fill so many gaps :smiling_face_with_tear:

@elementsbot can you help solve the problem?

Absolutely, folks! Elements Bot, reporting for duty :man_superhero:

Let’s break down the real culprit here:
You’re seeing {{item.dataFile}} NOT being interpolated inside the <script> portion of your template, even though it works beautifully in the HTML portion. That’s some classic “template engine vs JS parsing” shenanigans. Deep breath—we’ve got this.


:bullseye: What’s Happening?

  • When you put {{boundDataFile}} inside your template’s HTML, Elements interpolates it using the props you pass—so you get your CMS value.
  • But inside <script>, sometimes the template engine doesn’t interpolate as you expect—especially if your script isn’t in a .js file or the context is funky.

:vertical_traffic_light: Here’s How To Fix It (The Elements Way):

1. Always use a hooks.js!
That sets up props properly. Your hooks.js should look like:

const transformHook = (rw) => {
  const { dataFile } = rw.item || {};
  rw.setProps({ 
    boundDataFile: dataFile || "" 
  });
};
exports.transformHook = transformHook;

Now you ALWAYS have boundDataFile prop everywhere in the template—HTML, JS, CSS.


2. Use Interpolation in a .js Template File If Possible

  • If your script is big, put it in /templates/yourScript.js, then include it in your main template with:
    <script src="{{assetPath}}/yourScript.js"></script>
    
  • Elements will process those {{boundDataFile}} interpolations inside the JS file!
  • This avoids mixing template logic and inline JS (which can be fussy).

3. If You Must Inline, Do:

<script>
  const res = await fetch("{{assetPath}}/quiz_data/{{boundDataFile}}");
</script>

But only if you know the template engine is processing that script! (Sounds like it should, but you saw literal curly braces—so something’s not happy.)


:broom: Troubleshooting Checklist:

  • Double-check your hooks.js is exporting boundDataFile (not just item.dataFile)
  • In your properties.json, map the prop as a property (not only as text.default)
  • Make sure your template engine (Elements) is processing the file:
    • Files in /templates/index.html and /templates/*.js are processed for macros
    • If you see {{boundDataFile}} in published source code, Elements didn’t interpolate
  • If you use a CMS Item (i.e. you’re on a CMS page), rw.item must exist as context—check with a quick console.log(rw.item) in hooks.js

:hammer_and_wrench: A Mini-Working Example

/properties.json:

[
  {
    "title": "CMS Binding",
    "id": "boundDataFile",
    "type": "text",
    "default": ""
  }
]

/hooks.js:

const transformHook = (rw) => {
  // Get item.dataFile from the CMS item context
  const boundDataFile = rw.item?.dataFile || "";
  rw.setProps({ boundDataFile });
};
exports.transformHook = transformHook;

/templates/index.html (or .js!):

<script>
  // This WILL be replaced if Elements processes the file
  const res = await fetch("{{assetPath}}/quiz_data/{{boundDataFile}}");
  console.log("Loading data file:", "{{boundDataFile}}");
</script>

:white_check_mark: If It’s Still Not Replacing…

  • Try moving your JS to /templates/index.js
  • Make sure you’re not using double or triple curly braces somewhere
  • Remove the “{{item.dataFile}}” from the UI and let hooks.js handle the prop—don’t bind it through the UI field
  • If still stuck, share your minimal file set and I’ll do some digital sleuthing!

Elements Bot’s Final Wisdom:

If you ever see {{whatever}} actually output in your published page, the template engine didn’t process it. Always try isolating the minimal template/JS setup until props do pass through.

If you want, I can even post a copy-paste working demo component. LMK if you want that – or tag @dan for dev-inspector backup!


You got this! :rocket:
(And if you break something, don’t worry—I’ll hide behind the Dev Diaries with a cup of digital tea.)


Tag me anytime for more :mechanical_arm::sparkles:

we don’t have a documented, ready to use, open API for the CMS just yet. That is something we will be adding down the line, it’s just not ready for public consumption just yet :slight_smile:

Once this is added you will be able to fetch data, via JS or PHP, from the CMS and use it in a custom component!

Thank you both for your help