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

htmt

v0.0.3

Published

HyperText Markup Targets

Readme

HyperText Markup Targets (HTMT)

HyperText Markup Targets (HTMT) brings dynamic partial-page loading to plain HTML. Add a target attribute to links and forms, and HTMT handles the rest—no build tools, no framework, just declarative markup.

It's built on web standards: regular <a> and <form> elements, standard HTML attributes, and just enough JavaScript to orchestrate the behavior. Your HTML remains semantic, searchable, and resilient.

Tiny footprint: 794 bytes gzipped, 686 bytes minified + gzipped.

Quick Start

Stackblitz Playground

Try it out live on Stackblitz

Manual Setup

  1. Load the HTMT script as an ES module and initialize it on your document:
import { htmt } from "htmt";
htmt(document.body, true);
  1. Add target attributes to your links and forms:
<span id="results">...</span>

<a href="/search?q=example" target="results"> Search </a>
  1. Serve partial HTML responses with a matching target element:
<span id="results"> Found 42 results for "example" </span>

That's it. HTMT loads the response into a hidden iframe and swaps the target element.

Usage

Setup Parameters

htmt(root, install);
  • root – the DOM subtree to scan for enhanced elements. Usually document.body.
  • install – when true, automatically wires up global click and submit handlers to track interactions.

Targeted Links

Link with a target attribute to swap an element:

<span id="results">Click to load</span>

<a href="/api/search" target="results"> Load Results </a>

Server response:

<span id="results"> Here are your search results. </span>

HTMT creates a hidden iframe, loads the response there, and replaces the target element in the main document.

Forms

The same pattern works with forms (GET or POST):

<form method="post" action="/api/subscribe" target="message">
  <input type="email" name="email" />
  <button>Subscribe</button>
  <span id="message"></span>
</form>

Server response:

<span id="message"> Thanks for subscribing! </span>

Controlling Multiple Requests

Use the optional frame attribute to name the iframe. Multiple links/forms with the same frame name will share a single iframe—newer requests automatically cancel pending ones:

<a href="/api/page-1" target="results" frame="pages">Page 1</a>
<a href="/api/page-2" target="results" frame="pages">Page 2</a>

<div id="results"></div>

If frame is omitted, HTMT uses the target value as the frame name.

Executing Scripts in the Main Document

Wrap scripts in a <template> in the response <head> to delay execution until after the content is inserted into the main page:

<head>
  <template>
    <span id="results">
      Content with
      <script>
        console.log("Running in main frame");
      </script>
    </span>
  </template>
</head>

HTMT extracts templates, moves their content to the main document, and runs any scripts there.

If a template has a target attribute, HTMT treats it as an out-of-band (OOB) swap, replacing the specified element in the main document with the contents of the template.

Works Without JavaScript

Your links and forms remain fully functional HTML. Without HTMT loaded, users can still click a link with a target attribute—it opens the partial response in a new page or tab, just like normal.

This means you can respond with a complete HTML document. Include stylesheets, scripts, images—whatever makes the partial content self-contained. The same response works both as a partial swap (with HTMT) and as a standalone page (without HTMT).

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="/styles.css" />
    <script src="/interactive.js"></script>
  </head>
  <body>
    <span id="results">
      Your partial content here, complete with styles and scripts.
    </span>
  </body>
</html>

Attributes Reference

Required

  • target – the id of the element to replace with the response content.

Optional

  • frame – names the internal iframe. Links/forms sharing the same frame name reuse a single iframe. If omitted, target is used as the frame name.
  • busy – comma-separated list of element IDs to set aria-busy=true during the request (e.g., busy="submit-btn,status")
  • disabled – comma-separated list of element IDs to set disabled and aria-disabled during the request

Special values for busy and disabled:

  • _self – the element that triggered the request
  • _submitter – the button that submitted the form (form elements only)

Example:

<form
  method="post"
  action="/api/save"
  target="status"
  busy="_self"
  disabled="submit-btn,form-input"
>
  <input id="form-input" type="text" />
  <button id="submit-btn">Save</button>
  <div id="status"></div>
</form>

How It Works

When you initialize HTMT with install: true, it sets up global event listeners for clicks and form submissions. When it detects an element with a target attribute:

  1. Creates or reuses a hidden iframe (named by the frame attribute, or target if not specified)
  2. Directs the browser to load the URL into that iframe
  3. When the response loads, HTMT extracts any <template> elements from the response <head> and queues them for injection
  4. Locates the element matching the target ID in both the response and the main document
  5. Replaces the main document's element with the response element
  6. Executes any queued templates and their scripts in the main document context

Philosophy

HTMT is intentionally minimal. It adds just enough JavaScript to coordinate iframe-based loading and element swapping. The patterns are declarative—all behavior flows from your HTML structure. Your links and forms remain valid, semantic HTML that works without JavaScript; HTMT progressively enhances them.

This aligns with the web's foundational principle: start with semantic markup, layer on behavior. No build step required, no framework lock-in.

Status

This project is experimental. The API may evolve as we explore these patterns.