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.
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
| Need | Use |
|---|---|
| Add/remove a class | <script> |
| Wire a button to a one-line action | <script> |
| Anything with state, refs, or rendering | An island (framework component) |
| Anything where DOM diffing helps | An 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 →