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

astro-create-portal

v1.0.2

Published

Astro integration providing head-safe and keyed portal components for SSR and static builds.

Downloads

6

Readme


Features

  • SSR & static ready: works during astro dev, hybrid SSR, and fully static builds without custom scripts.
  • Head deduplication: merges <meta>, <link>, <script>, and <style> tags by computed keys so only the latest unique entry survives.
  • Keyed portals: reuse CreatePortal and Portal to bridge markup between distant parts of the DOM using a shared key.
  • No hydration overhead: all components render to <template> tags and execute lightweight client-side runtime only when necessary.

Installation

Install with your preferred package manager. The package declares Astro as a peer dependency (>= 5).

npm install astro-create-portal
# or
pnpm add astro-create-portal
# or
bun add astro-create-portal

Once installed, register the integration in astro.config.mjs:

import astroPortal from 'astro-create-portal';

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

Quickstart

  1. Add <HeadPortal> inside your page/component to push markup into the document head.
  2. Use <CreatePortal key="..." /> to define a target location.
  3. Render <Portal key="...">...</Portal> elsewhere to project content to that target.

The runtime script is injected automatically in development; during builds, markup is hoisted and deduplicated ahead of time.


Components

HeadPortal

Wrap any head-friendly markup in <HeadPortal> to duplicate the rendered output into the <head> element. Supports <title>, <meta>, <link>, <style>, <script>, and <base> tags. Duplicate entries are eliminated based on deterministic keys.

CreatePortal

<CreatePortal key="sidebar" /> declares a named insertion point. At build time it is replaced with a comment marker, and during hydration-free runtime the first matching marker receives the projected content.

Portal

<Portal key="sidebar">...children...</Portal> moves its slot content to the matching CreatePortal marker. The move happens after DOMContentLoaded and on every astro:page-load navigation in dev/SPA modes.

Each component validates the key prop (string/number/bigint) and throws an informative error if it is missing or empty.


Runtime behaviour

  • Injected only in development: the runtime script scans for <template> markers and performs DOM moves without hydration islands.
  • On first render and on SPA navigations (astro:page-load), head portals are hoisted before ordinary portals resolve.
  • document.head entries are deduplicated by tag type and identifying attributes (name, property, rel, href, etc.), ensuring predictable metadata.

Build integration

During astro build, the integration:

  1. Loads each generated HTML page with JSDOM.
  2. Hoists <HeadPortal> markup into <head> and deduplicates entries.
  3. Resolves keyed portals by cloning their content into the corresponding marker position.
  4. Writes the transformed HTML back to disk so no client runtime is needed for static hosting.

Example (Astro)

---
import { HeadPortal, CreatePortal, Portal } from 'astro-create-portal';
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Portal Demo</title>
  </head>
  <body>
    <HeadPortal>
      <title>Contact</title>
      <meta name="description" content="Reach out to the team" />
      <link rel="canonical" href="https://example.com/contact" />
    </HeadPortal>

    <CreatePortal key="footer-contact" />

    <main>
      <h1>Welcome</h1>
      <Portal key="footer-contact">
        <aside>
          <h2>Contact</h2>
          <p>Email us at [email protected]</p>
        </aside>
      </Portal>
    </main>
  </body>
</html>

FAQ

Is this inspired by React's createPortal?
Yes! The API and keyed approach take cues from React's portal primitives, reworked to align with Astro's server-first rendering and static output pipeline.

Do portals support multiple targets with the same key?
Only the first CreatePortal with a given key is used. Define unique keys for each target.

Can I portal into the <head>?
Use <HeadPortal> for head content. Regular portals operate within the document body.

Does it work with view transitions or SPA mode?
Yes. The runtime reprocesses templates on astro:page-load, covering client-side navigations.


License

This project is licensed under the MIT License.


Happy portaling!