Jehlani Luciano Logo

Scripts and Events

Guidelines for using client-side JavaScript `<script>` tags and handling events in Astro.

astro
          
            ## Astro Scripts and Event Handling Guidelines

1.  Purpose: Add client-side interactivity (event listeners, animations, analytics, etc.) directly within `.astro` components using standard HTML `<script>` tags.

2.  Default Behavior (`<script>` tag):

    - Processed & Bundled: Astro processes scripts by default. It bundles imports (local files, npm modules) and handles TypeScript.
    - Module Scope: Output script uses `type="module"`. This means it's deferred (non-blocking), scoped (doesn't pollute global namespace), and runs after HTML parsing.
    - Deduplication: If a component with a script is used multiple times on a page, the script itself is only included once in the final HTML output.

3.  Opting Out of Processing (`is:inline`):

    - Use the `is:inline` directive on the `<script>` tag (`<script is:inline>`) to prevent Astro from processing it.
    - The script content is rendered _exactly_ as written into the HTML.
    - Imports will _not_ work. TypeScript is _not_ processed.
    - The script will be duplicated if the component is used multiple times.
    - Use this for simple inline scripts or when loading external scripts via `src` that shouldn't be bundled.
    - Note: Adding `type="module"` or most other attributes (except `src`) to a `<script>` tag implicitly applies `is:inline`.

4.  Loading Script Files:

    - Local Scripts (in `src/`): Reference using a relative path in the `src` attribute (`<script src="../scripts/my-script.ts"></script>`). Astro processes, bundles, and optimizes these.
    - External/Public Scripts (in `public/` or CDN): Reference using an absolute path (for `public/`) or full URL. Must use `is:inline` (`<script is:inline src="/scripts/public-script.js"></script>`). Astro does _not_ process these.

5.  Event Handling:

    - Astro uses standard browser APIs. No custom event syntax (like `onClick`).
    - Use `document.addEventListener` or `element.addEventListener` within a `<script>` tag to attach listeners.
    - Use `document.querySelectorAll` to target multiple elements if the script is part of a reusable component.

6.  Custom Elements (Web Components):

    - Define custom element behavior using `class MyElement extends HTMLElement { ... }` and register with `customElements.define('my-element', MyElement)` inside a `<script>` tag.
    - Wrap the component's HTML template in the custom tag (`<my-element>...</my-element>`).
    - Benefits:
      - Encapsulates logic and DOM querying (`this.querySelector` vs. `document.querySelector`).
      - Initialization logic (`connectedCallback`) runs for _each instance_ of the element, even though the defining `<script>` only runs once per page.
    - Recommended for reusable interactive components built solely with vanilla JS.

7.  Passing Data from Server (Frontmatter) to Client (Script):

    - Server-side variables in the `---` script are not directly accessible in client-side `<script>` tags.
    - Use data-\* attributes on HTML elements to store server-side values.
      ```astro
      ---
      const serverMessage = "Hello from server!";
      ---
      <div data-message={serverMessage}>...</div>
      <script>
        const div = document.querySelector('div');
        const clientMessage = div.dataset.message; // "Hello from server!"
        console.log(clientMessage);
      </script>
      ```
    - Custom elements can access these via `this.dataset.attributeName`.

8.  Interaction with UI Frameworks: If a `<script>` needs to interact with elements rendered by a UI framework component (island), consider using Custom Elements, as the framework component might not be hydrated/available when the standard script runs initially.

Reference: [Astro Scripts and Event Handling Docs](https://docs.astro.build/en/guides/client-side-scripts/)