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 🙏

© 2025 – Pkg Stats / Ryan Hefner

hytypemedia

v0.2.1

Published

Minimal typed HTML templating helpers for Hono/Workers/HTMX. JSX-free, type-safe HTML generation with automatic escaping.

Readme

HyTypeMedia

npm version TypeScript License: MIT

Minimal typed HTML templating helpers specifically designed for Hono, Cloudflare Workers, and HTMX. A JSX-free approach to HTML templating that generates safe, escaped HTML strings with full TypeScript support.

Features

  • Type-Safe: Full TypeScript support with IntelliSense for all HTML elements and attributes
  • Secure: Automatic HTML escaping prevents XSS attacks
  • Minimal: Zero dependencies, optimized for edge runtimes
  • Complete: Support for all HTML5 elements (67+ elements)
  • Targeted: Built specifically for Hono/Workers/HTMX workflows
  • Modern: ESM-first with tree-shaking support

Quick Start

npm install hytypemedia
import { div, h1, p, button, html, head, title, body } from 'hytypemedia';

// Simple elements
const greeting = h1('Hello, World!');
// → <h1>Hello, World!</h1>

// Elements with attributes
const styledDiv = div({ class: 'container', id: 'main' }, 'Content');
// → <div class="container" id="main">Content</div>

// Complete HTML documents
const page = html({ lang: 'en' },
  head(title('My Page')),
  body(
    h1('Welcome'),
    p('This is a typed HTML template.')
  )
);

Core Concepts

Element Functions

Every HTML tag has a corresponding function:

import { div, span, a, img, input } from 'hytypemedia';

div('Hello')                           // <div>Hello</div>
span({ class: 'highlight' }, 'Text')   // <span class="highlight">Text</span>
a({ href: '/home' }, 'Home')           // <a href="/home">Home</a>
img({ src: 'logo.png', alt: 'Logo' }) // <img src="logo.png" alt="Logo">
input({ type: 'email', required: true }) // <input type="email" required>

Automatic Escaping

All content is automatically escaped for security:

import { div, raw } from 'hytypemedia';

// Automatically escaped
div('<script>alert("xss")</script>')
// → <div>&lt;script&gt;alert("xss")&lt;/script&gt;</div>

// Explicit raw HTML (use only with trusted content)
div(raw('<strong>Bold</strong>'))
// → <div><strong>Bold</strong></div>

Class and Style Normalization

import { div } from 'hytypemedia';

// String classes
div({ class: 'btn primary' })
// → <div class="btn primary"></div>

// Array classes
div({ class: ['btn', 'primary'] })
// → <div class="btn primary"></div>

// Object classes (conditional)
div({ class: { btn: true, primary: true, disabled: false } })
// → <div class="btn primary"></div>

// Object styles (camelCase → kebab-case)
div({ style: { fontSize: '16px', backgroundColor: 'blue' } })
// → <div style="font-size:16px;background-color:blue"></div>

Content Types

Supports multiple content types:

import { div } from 'hytypemedia';

div('string')              // Strings
div(42)                    // Numbers  
div(['hello', ' ', 'world']) // Arrays (flattened)
div(null)                  // null/undefined (ignored)
div(raw('<em>html</em>'))  // SafeHtml objects

Advanced Usage

Document Structure with Direct Nesting

import { html, head, meta, title, body, h1, p } from 'hytypemedia';

const document = html({ lang: 'en' },
  head(
    meta({ charset: 'utf-8' }),
    meta({ name: 'viewport', content: 'width=device-width, initial-scale=1' }),
    title('My App')
  ),
  body(
    h1('Welcome to My App'),
    p('Built with HyTypeMedia')
  )
);

Forms and Inputs

import { form, label, input, textarea, button, select, option } from 'hytypemedia';

const contactForm = form({ method: 'post', action: '/contact' },
  label({ for: 'email' }, 'Email:'),
  input({ 
    type: 'email', 
    id: 'email', 
    name: 'email', 
    required: true 
  }),
  
  label({ for: 'message' }, 'Message:'),
  textarea({ 
    id: 'message', 
    name: 'message', 
    rows: 4 
  }),
  
  select({ name: 'country' },
    option({ value: 'us' }, 'United States'),
    option({ value: 'uk' }, 'United Kingdom')
  ),
  
  button({ type: 'submit' }, 'Send Message')
);

Data Attributes and ARIA

import { button, div } from 'hytypemedia';

// Data attributes
const component = div({
  'data-testid': 'user-card',
  'data-user-id': 123,
  'data-active': 'true'
});

// ARIA attributes
const accessibleButton = button({
  'aria-label': 'Close dialog',
  'aria-expanded': false,
  'aria-controls': 'menu'
}, '×');

Custom Elements

import { customElement } from 'hytypemedia';

const myCustomElement = customElement('my-component');
const result = myCustomElement({ prop: 'value' }, 'Content');
// → <my-component prop="value">Content</my-component>

Framework Integration

Hono Example

import { Hono } from 'hono';
import { div, h1, p, a } from 'hytypemedia';

const app = new Hono();

app.get('/', (c) => {
  const html = div({ class: 'container' },
    h1('Welcome to My Site'),
    p('Built with Hono and HyTypeMedia'),
    a({ href: '/about' }, 'Learn More')
  );
  
  return c.html(html);
});

HTMX Integration

import { div, button, form, input } from 'hytypemedia';

const htmxForm = form({
  'hx-post': '/api/users',
  'hx-target': '#user-list',
  'hx-swap': 'beforeend'
},
  input({ type: 'text', name: 'name', placeholder: 'Enter name' }),
  button({ type: 'submit' }, 'Add User')
);

const htmxButton = button({
  'hx-get': '/api/data',
  'hx-target': '#content',
  'hx-indicator': '#spinner'
}, 'Load Data');

API Reference

Element Functions

All HTML5 elements are available as functions:

Text Content: h1, h2, h3, h4, h5, h6, p, span, div, a, strong, em, code, etc.

Form Elements: form, input, textarea, select, option, button, label, fieldset, legend

Table Elements: table, thead, tbody, tfoot, tr, th, td, caption, colgroup, col

Media Elements: img, video, audio, source, track, canvas, svg

Semantic Elements: main, section, article, aside, header, footer, nav, figure, figcaption

Meta Elements: head, title, meta, link, style, script, base

Helper Functions

Fragment(...children)

Combines multiple elements without a wrapper:

Fragment(h1('Title'), p('Content'))
// → <h1>Title</h1><p>Content</p>

doctype()

Returns HTML5 doctype:

doctype()
// → <!doctype html>

raw(html)

Marks HTML as safe (no escaping):

raw('<strong>Bold</strong>')
// → <strong>Bold</strong>

safeText(text)

Explicitly escapes text:

safeText('<script>')
// → &lt;script&gt;

customElement(tagName)

Creates a custom element function:

const myEl = customElement('my-element');
myEl({ prop: 'value' }, 'content')
// → <my-element prop="value">content</my-element>

Types

// Main function type
type HtmlFunction = (...args: [GlobalAttrs, ...Content[]] | Content[]) => string;

// Global attributes
type GlobalAttrs = {
  id?: string;
  class?: ClassValue;
  style?: StyleValue;
  role?: string;
  tabindex?: number;
  title?: string;
  [k: `data-${string}`]: unknown;
  [k: `aria-${string}`]: unknown;
} & Record<string, unknown>;

// Class values
type ClassValue = string | string[] | Record<string, boolean>;

// Style values  
type StyleValue = string | Record<string, string | number>;

// Content types
type Content = string | number | boolean | null | undefined | SafeHtml | Content[];

Browser Support

  • Node.js: >= 18.0.0
  • TypeScript: >= 4.9.0
  • Runtimes: Node.js, Cloudflare Workers, Deno, Bun
  • Bundlers: Vite, esbuild, Rollup, Webpack, Parcel

Contributing

  1. Fork the repository
  2. Create your feature branch: git checkout -b feature/amazing-feature
  3. Run tests: npm test
  4. Commit your changes: git commit -m 'Add amazing feature'
  5. Push to the branch: git push origin feature/amazing-feature
  6. Open a Pull Request

Development

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

# Type checking
npm run typecheck

# Linting
npm run lint

License

MIT © Terence Stupple

Related Projects