Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Scripts in components

Not every button needs React. For simple behaviour (opening a menu, copying a link, switching a theme) ordinary JavaScript in a <script> tag inside an .astro component is enough. Astro processes such a script during the build and attaches it to the page.

Create src/components/CopyLink.astro:

<button class="copy-link">Copy link</button>
 
<script>
  const buttons = document.querySelectorAll('.copy-link');
 
  buttons.forEach((button) => {
    button.addEventListener('click', async () => {
      await navigator.clipboard.writeText(location.href);
      button.textContent = 'Copied';
    });
  });
</script>

Put <CopyLink /> into the post page src/pages/posts/[id].astro after <Content />. Open a post and click the button: the page address goes to the clipboard and the label changes to "Copied".

What Astro does with the script

FeatureWhat it means
ModuleThe script works as type="module": you can use import
TypeScriptYou can write TypeScript inside
OnceIf the component appears three times, the script is included once
BundlingThe code is bundled and minified into a separate file

Because of "once", the script finds all buttons with querySelectorAll rather than one: that way it serves every instance of the component.

Data from the frontmatter

The script runs in the browser and cannot see frontmatter variables. Data is passed through data-* attributes:

---
const { title } = Astro.props;
---
 
<button class="share" data-title={title}>Share</button>
 
<script>
  document.querySelectorAll<HTMLButtonElement>('.share').forEach((button) => {
    button.addEventListener('click', () => {
      const title = button.dataset.title;
      navigator.share({ title, url: location.href });
    });
  });
</script>

The title prop goes into an HTML attribute, and the script reads it from dataset. In browsers with a share menu, a click opens it with the post title. The <HTMLButtonElement> hint tells TypeScript that buttons were found; otherwise the editor does not know about dataset.

A script without processing

The is:inline attribute leaves a script as is: Astro does not bundle it and inserts it wherever the component appears. This is how third-party snippets, such as a visit counter, are added. A script from the public/ folder loaded with the src attribute is also marked is:inline.

Common mistake

People look up an element with document.querySelector('button') and get the wrong button: the first one on the page rather than the one in their component. Give the component's elements a unique class or a data-* attribute and select by it.

Thanks for reading!

Donate to the site — we work hard for you.