npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@magnet-js/astro

v0.1.0

Published

Astro integration for Magnet.

Readme

@magnet/astro

Astro integration for Magnet.

@magnet/astro lets you use Magnet components inside Astro pages: server-side rendering to static HTML, plus client-side hydration through Astro's client:* directives. No JSX — components are plain functions that build real DOM trees through the Magnet context.

Install

JSR

deno add jsr:@magnet/astro

npm via JSR

npx jsr add @magnet/astro

Setup

Register the integration in astro.config.*:

import { defineConfig } from "astro/config";
import magnet from "@magnet/astro";

export default defineConfig({ integrations: [magnet()] });

The integration registers the Magnet server renderer and client hydrator with Astro. All built-in hydration directives work out of the box: client:load, client:idle, client:visible, client:media, and client:only="magnet".

Options

magnet({
  signals: "preact", // "tc39" (default) | "preact" | "alien"
  rendererName: "magnet", // id matched by client:only directives
  extender: "./src/magnet-context.ts", // optional context extender module
  adopt: true, // adopt server-rendered DOM during hydration (default)
});
  • signals — the signal implementation used to create component contexts on the server and in the browser. Each adapter ships in its own entrypoint (@magnet/astro/server/server.preact, @magnet/astro/client/client.preact, and so on), so the client bundle contains exactly one signal implementation. Set it to match the adapter your Magnet application code uses.

  • rendererName — the name matched by client:only directives in Astro pages. Defaults to "magnet".

  • extender — module specifier of a context extender applied to every component context, on the server and in the browser. Use it when your components expect an extended context created with m.extend():

    // src/magnet-context.ts
    export default (m) => m.extend({ analytics: track });

    Relative specifiers resolve against the project root. The context is created and extended once per signal implementation and shared across all islands.

  • adopt — whether islands adopt their server-rendered DOM during hydration, attaching listeners and reactive subscriptions to the existing nodes instead of re-rendering from scratch. Defaults to true. When the tree does not match the component's render (third-party DOM changes, extensions, user input before hydration), the hydrator falls back to a fresh render, so correctness never depends on the tree being untouched.

  • name — the integration's name in Astro's config and logs (defaults to @magnet/astro).

  • clientEntrypoint / serverEntrypoint — override the registered entrypoint specifiers when the package is installed under a different name (for example a scoped npm build).

Writing components

A Magnet Astro component is a function wrapped in component(). The brand gives the renderer's check hook a deterministic, side-effect-free way to recognize Magnet components (JSX renderers must test-render components to detect them; Magnet does not need to).

// src/components/Counter.ts
import { component } from "@magnet/astro";

export default component((m, props, slots) => {
  const count = m.state(0);
  return m.html.div({ class: "counter" }, [
    m.html.button(
      { onclick: () => count.set(count.get() + 1) },
      ["count: ", count],
    ),
    slots.default,
  ]);
});

The component receives:

  • m — the Magnet context (html, svg, mathML, custom, render, text, comment, shadow, extend, and the signal primitives selected by the signals option). On the server it is bound to @magnet/ssr's string-rendering window; in the browser to the real window.
  • props — the props passed from the Astro page.
  • slots — the page's rendered slots as placeable node arrays (see below).

It returns a node, an array of nodes, a string/number rendered as text, or null/undefined to render nothing. Components may be async — the renderer awaits them.

Use it in an .astro page like any framework component:

---
import Counter from "../components/Counter.ts";
---

<Counter client:load>
  <p>rendered inside the counter</p>
</Counter>

How Astro slots map onto Magnet

Astro and Magnet have fundamentally different component models, and slots are where they meet:

  • Astro renders slot content itself and hands each framework renderer the result as an opaque HTML string per slot. Renderers place that markup by wrapping it in <astro-slot> elements (hydrated components) or <astro-static-slot> elements (static components); Astro strips the static wrappers from final output and reads the hydration wrappers back inside <astro-island> to distribute slot DOM on the client.
  • Magnet has no virtual DOM and no props/children indirection: components build real nodes, and children arrays accept nodes, text, signals, and cleanup functions — not raw HTML strings (strings are escaped as text).

The integration bridges this by turning each slot into an opaque placeholder node that components drop into children arrays:

component((m, props, slots) =>
  m.html.div({ class: "layout" }, [
    m.html.aside({}, [slots.sidebar]),
    m.html.main({}, [slots.default]),
  ])
);
  • slots.default is the unnamed slot; named slots appear under their name.
  • A slot the page did not provide is undefined — safe to spread into children arrays, since Magnet skips undefined children.
  • On the server each placeholder stringifies to the slot markup wrapped in the <astro-slot>/<astro-static-slot> marker Astro expects, so slots land exactly where the component placed them. On the client each placeholder is an <astro-slot> element whose children are parsed from the markup with the browser's native <template> parsing.
  • Placeholders are text-shaped, so they type-check anywhere textual children are accepted. Do not put them inside textual-only elements (title, textarea): their markup is raw HTML and is never escaped.

Hydration

All built-in directives work once the renderer is registered: client:load, client:idle, client:visible, client:media, and client:only="magnet".

By default, islands adopt their server-rendered DOM: the component runs against a cursor that serves the existing nodes in deterministic render order, so event listeners, attributes, and reactive subscriptions attach to the nodes already in the page. If anything changed the tree since SSR (third-party scripts, browser extensions, autofill, user input before hydration), the cursor reports a mismatch and the hydrator falls back to a fresh client render that replaces the island's content. Correctness never depends on the tree being untouched; adoption only affects whether nodes are reused or recreated. Disable adoption per project with adopt: false.

Constraints at the island boundary:

  • Props must be serializable by Astro (JSON-like values) to reach the client. During server-only rendering, props may be anything, including signals and functions.
  • _use actions and event listeners only run in the browser — that is Magnet's own SSR semantics, not a limitation of this package.
  • Signal-based children render their initial values on the server. Non-leading signal children are anchored by <!----> comment markers; leading signal children render bare.
  • Unmounting (for example after view transitions) runs the tree's full cleanup through Astro's astro:unmount event.

Public API

Entry point @magnet/astro:

  • magnetAstro (also the default export) — creates the Astro integration.
  • IntegrationOptionsname, rendererName, signals, extender, adopt, clientEntrypoint, serverEntrypoint.
  • component — brands a function as a Magnet component (re-exported from @magnet/common with the signature narrowed to MagnetComponent).
  • isComponent — detects branded components.
  • MagnetComponent / ComponentContext / ComponentResult — the component contract types.
  • ContextExtender — the extender function type.
  • Slots / SlotValue / SlotNode — slot placeholder types.
  • ComponentChildren / ComponentTagFor / ComponentHTML — tag types whose children accept any element (unlike HTMLChildren from @magnet/ui, which locks element children to the parent's own tag name).
  • ClientHydrator / IslandHydrate — the client hydrator types. Astro's own contract types (AstroIntegration, AstroComponentMetadata, and friends) come from the astro package, which is a peer dependency.

Entry points @magnet/astro/server/server.* (one per signal adapter:

server.tc39, server.preact, server.alien):

  • default export — the SSR renderer (check, renderToStaticMarkup, supportsAstroStaticSlot) registered with Astro as serverEntrypoint.
  • createServerRenderer — the renderer factory, for composing custom entrypoints.

Entry points @magnet/astro/client/client.* (one per signal adapter:

client.tc39, client.preact, client.alien):

  • default export — the client hydrator registered as clientEntrypoint.
  • createHydrator / sharedHydrator — hydrator factories for custom entrypoints, tests, and non-standard environments.

License

MIT © 2026 Fernando G. Vilar.