Islands are not Web Components: two different kinds of on-demand loading
Islands decide at build time where JavaScript is needed and when it hydrates. Web Components give the browser a self-contained element at runtime. Confusing them picks the wrong tool.
The two terms show up in the same paragraph so often that they read like the same idea: load things only when needed. They actually work at different layers. One governs build output and load timing, the other encapsulation and runtime.
Islands: build-time splitting and hydration timing
An Astro island is a build-time concept. Each interactive component becomes its own chunk while the rest of the page stays plain HTML:
<SearchBox client:idle />
<Comments client:visible />
client:idle and client:visible choose when to hydrate (after the main thread settles, or on entering the viewport). They do not decide whether to download anything. Components without a hydration directive, and every static part of the page, ship zero JavaScript.
Web Components: a runtime element
Custom elements are a browser primitive. Register customElements.define('x-counter', ...) and <x-counter> in the markup has behaviour, with Shadow DOM optionally bringing its own style isolation. No build tool, no framework, works on any page.
What it is not is a lazy-loading mechanism. The definition script either loads up front or you import() it yourself at some point. Nothing about inserting an <x-counter> makes the browser fetch the definition that implements it.
Why does <my-counter> look so much like an island
Because the rendered output is identical: a custom tag in the markup. The difference is who brings it to life. An island is rendered by a framework mounting or hydrating in the browser; a Web Component is resolved by the browser looking the tag up in the custom element registry. The first depends on your build output, the second on a definition script that is already loaded.
Why this site does not use Web Components
Two reasons. Styling first: the visual direction is driven by tokens on :root, and crossing a Shadow DOM boundary leaves only custom property inheritance and ::part, which is more ceremony than writing SCSS directly. Division of labour second: on-demand loading here is already covered by islands, and adding a second component registry only adds a second loading path to reason about.
Web Components genuinely shine for cross-framework reuse: one component that has to appear in a Vue page, an old jQuery admin and someone else is static site, or a widget embedded into a third-party page.
Islands answer when to load. Web Components answer who encapsulates. The same page can use both.

Comments
…