Plain `<script>` Tags

When You Don't Need a Framework

Plain `<script>` Tags

An inline `<script>` in an `.astro` file runs in the browser. Astro bundles, dedupes, and ships it once per page.

4 min read Level 2/5 #astro#scripts#vanilla-js
What you'll learn
  • Add a `<script>` for client-side behavior
  • Use `is:inline` for un-bundled scripts
  • Know when a script beats an island

For lightweight behavior — toggling a class, wiring a button to localStorage, attaching a listener — you often don’t need a framework. A plain <script> tag inside an .astro file works.

A Simple Script

<button id="theme-toggle">Toggle theme</button>

<script>
  document.getElementById("theme-toggle")?.addEventListener("click", () => {
    document.documentElement.classList.toggle("dark");
  });
</script>

That’s the whole thing. No client directive, no framework.

How Astro Treats It

By default, Astro bundles <script> tags through Vite:

  • TypeScript and imports work
  • Multiple components with the same script share one bundle
  • The script gets a type="module" and proper defer behavior

Multiple Scripts In One Component

You can have several <script> tags. Astro bundles them together.

<script>
  // a small helper
  function $(sel) { return document.querySelector(sel); }
</script>

<script>
  // uses the helper
  $("#go")?.addEventListener("click", () => alert("hi"));
</script>

Order isn’t guaranteed across separate tags — keep one tag per component when behavior matters.

With Imports

<script>
  import { animate } from "../lib/animate.ts";
  document.querySelectorAll(".reveal").forEach(el => animate(el));
</script>

Imports work just like any module. Vite handles the bundling.

is:inline — Skip Bundling

If you need a script untouched (an analytics snippet, a third-party inline script), use is:inline:

<script is:inline>
  // shipped EXACTLY as written, no bundling
  window.GA = { id: "UA-…" };
</script>

Caveats:

  • No TypeScript or imports (it’s not processed)
  • No deduplication — placed wherever you put it
  • Use sparingly — bundling is almost always what you want

When To Use A Script vs An Island

NeedUse
Add/remove a class<script>
Wire a button to a one-line action<script>
Anything with state, refs, or renderingAn island (framework component)
Anything where DOM diffing helpsAn island
One global behavior site-wide<script> in the layout

Up Next

When two islands need to share data — the state-sharing question.

Sharing State Between Islands →