#<script> vars

16 messages · Page 1 of 1 (latest)

hollow root
#

How can I pass a variable to a <script> from --- ?

solid oarBOT
#

Hi @hollow root. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Passing Frontmatter Variables to <script>

Since frontmatter runs on the server and is not available in the browser, you need a workaround to pass variables to client-side <script> tags. The recommended approach is using data-* attributes.

Recommended: data-* attributes

Store your frontmatter variable as a data-* attribute on an HTML element, then read it in your script via dataset:

---
const { message = 'Welcome, world!' } = Astro.props;
---

<astro-greet data-message={message}>
  <button>Say hi!</button>
</astro-greet>

<script>
  class AstroGreet extends HTMLElement {
    connectedCallback() {
      const message = this.dataset.message;
      const button = this.querySelector('button');
      button.addEventListener('click', () => {
        alert(message);
      });
    }
  }
  customElements.define('astro-greet', AstroGreet);
</script>

This approach works well when the component is used multiple times with different values. [Pass variables to scripts]


Alternative: define:vars (with caveats)

You can use the define:vars directive to pass variables directly:

---
const message = "Astro is awesome!";
---

<script define:vars={{ message }}>
  alert(message);
</script>

⚠️ Caution: Using define:vars on a <script> implies is:inline, meaning the script won't be bundled and will be inlined into the HTML. This also means the script reruns for every instance of the component, which may not be desirable.

[define:vars directive]


#

In summary: Prefer the data-* attribute pattern for scripts, and reserve define:vars for <style> tags where it works without the bundling limitation.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@hollow root Kapa.ai is still learning and improving, please let me know how I did by reacting below.

hollow root
#

@solid oar Why can't I just define a var at the top to be bundled into script at the bottom?

solid oarBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The core reason is that frontmatter runs on the server at build/request time, while <script> tags run in the browser. They are two completely separate execution environments — the frontmatter code is stripped out entirely before the page reaches the browser, so there's no shared scope between them. [Astro components]

Additionally, Astro processes and bundles <script> tags independently — a script may be deduplicated and included only once on a page even if the component is used multiple times. If a variable from frontmatter were simply injected at the top of a bundled script, it would break this model since each instance of the component could have a different value for that variable. [define:vars directive]

This is exactly why define:vars forces is:inline on <script> tags — it has to opt out of bundling so the script can re-run with each component instance's specific values.

The data-* attribute pattern sidesteps all of this cleanly: the value is baked into the HTML (which the server can control), and the single bundled script reads it at runtime from the DOM. [Pass variables to scripts]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@hollow root Kapa.ai is still learning and improving, please let me know how I did by reacting below.

hollow root
#

@solid oar So I can't put my constants into a separate file to be used in a <script> ?

solid oarBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

You actually can import from a separate file in a <script> tag — but the constants need to be defined in a regular .js/.ts file in your src/ folder, not sourced from your frontmatter.

<!-- my-component.astro -->
<script>
  import { MY_CONSTANT } from '../constants/myConstants.js';

  console.log(MY_CONSTANT);
</script>

Astro will process, bundle, and optimize that import automatically. [Script processing]

You can also import an entire .js/.ts script file directly:

<script src="../scripts/my-script.js"></script>

[Script processing]


What you can't do is share constants that are defined in the frontmatter (---) of an .astro file, since that code only runs on the server and is never available in the browser. If you need a constant in both places, the solution is to define it in a standalone .js/.ts file and import it in both your frontmatter and your <script> tag independently.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@hollow root Kapa.ai is still learning and improving, please let me know how I did by reacting below.

hollow root
#

@solid oar does that mean I can import functions too and they will be bundled?

solid oarBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes! The knowledge sources confirm that Astro's script processing supports importing from local files and npm packages, and they will all be bundled together. [Script processing]

So you can do things like:

<script>
  // Import a function from a local file
  import { myFunction } from '../utils/helpers.js';

  // Import from an npm package
  import confetti from 'canvas-confetti';

  myFunction();
  confetti();
</script>

Astro will:

  • Bundle all the imports together
  • Deduplicate the script if the component is used multiple times on a page
  • Optimize the output
  • Support TypeScript out of the box

[Script processing]

The key requirement is that your script tag has no extra attributes (other than src), otherwise Astro treats it as unprocessed and imports won't be resolved. [Unprocessed scripts]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: