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

micro-js-html

v0.0.8

Published

HTML as JavaScript library for client and server

Downloads

230

Readme

micro-js-html

It's actually just JavaScript - A lightweight, reactive HTML-as-JavaScript library for both client and server-side rendering.

Version Node License

🚀 Features

  • 🏃‍♂️ Lightweight - Minimal footprint, maximum performance
  • ⚡ Reactive State Management - Built-in reactive state with automatic re-rendering
  • 🔄 Universal - Works seamlessly on both client and server
  • 📱 Modern - ES6+ modules with full TypeScript support
  • 🎯 Simple - Intuitive API with plain old JavaScript
  • 🔧 Extensible - Easy integration with micro-js ecosystem
  • ⚡ Fast - <50ms load time from local server to first DOM render
  • 🧪 Well Tested - Comprehensive test coverage

📦 Installation

npm install micro-js-html

🎯 Quick Start

Basic HTML Generation

import { htmlTags } from 'micro-js-html'
const { html, body, div, h1, p, a } = htmlTags

// Create HTML structure
const page = html(
  body(
    div({ class: 'container' },
      h1('Welcome to micro-js-html'),
      p('Build web apps with just JavaScript!'),
      a({ href: 'https://github.com' }, 'Learn More')
    )
  )
)

console.log(page.render())
// Output: <html><body><div class="container">...</div></body></html>

Reactive State Management

import { createState, htmlTags } from 'micro-js-html'
const { div, h1, p, button } = htmlTags

// Create reactive state
const state = createState({ 
  count: 0, 
  title: 'Counter App' 
})

// Create reactive component
const Counter = () => div(
  h1(state.get('title')),
  p(`Count: ${state.get('count')}`),
  button({ 
    onclick: () => state.set('count', state.get('count') + 1) 
  }, 'Increment')
)

// Watch for state changes
state.watch((data) => {
  console.log('State updated:', data)
  // Re-render component automatically
})

📚 Core Concepts

HTML Elements

All standard HTML elements are available as JavaScript functions:

import { htmlTags } from 'micro-js-html'
const { 
  // Structure
  html, head, body, div, span, section, article, header, footer,
  // Text
  h1, h2, h3, h4, h5, h6, p, strong, em, mark, code,
  // Forms  
  form, input, textarea, button, select, option, label,
  // Media
  img, audio, video, source, figure, figcaption,
  // Lists
  ul, ol, li, dl, dt, dd,
  // Tables
  table, thead, tbody, tfoot, tr, th, td
} = htmlTags

// Elements with attributes
const element = div({ 
  id: 'myDiv', 
  class: 'container',
  'data-value': '123' 
}, 'Content here')

// Nested elements
const page = html(
  head(
    title('My App'),
    meta({ charset: 'utf-8' })
  ),
  body(
    header(h1('Welcome')),
    main(
      section(
        p('This is a paragraph'),
        button({ onclick: handleClick }, 'Click me')
      )
    )
  )
)

Event Handling

// Event handlers are automatically bound
const interactive = div(
  button({ 
    onclick: (event) => console.log('Clicked!'),
    onmouseover: (event) => console.log('Hovered!')
  }, 'Interactive Button'),
  
  form({ 
    onsubmit: (event) => {
      event.preventDefault()
      console.log('Form submitted!')
    }
  },
    input({ type: 'text', onchange: (e) => console.log(e.target.value) }),
    button({ type: 'submit' }, 'Submit')
  )
)

🔄 State Management

Creating State

import { createState } from 'micro-js-html'

// Simple state
const appState = createState({
  user: { name: 'John', email: '[email protected]' },
  theme: 'dark',
  notifications: []
})

// Get values
const userName = appState.get('user').name
const allState = appState.getAll()

// Set values (triggers watchers)
appState.set('theme', 'light')

// Batch updates (single notification)
appState.update({
  theme: 'light',
  user: { ...appState.get('user'), name: 'Jane' }
})

Watching State Changes

// Watch all state changes
const unwatch = appState.watch((stateData) => {
  console.log('State changed:', stateData)
  updateUI(stateData)
})

// Cleanup when done
unwatch()

// Batch multiple updates
appState.batch((state) => {
  state.set('theme', 'dark')
  state.set('user', { name: 'Alice', email: '[email protected]' })
  // Only triggers one notification
})

Computed Values

// Create computed values with caching
const fullNameComputed = appState.computed('fullName', 
  (state) => `${state.user.firstName} ${state.user.lastName}`,
  ['user'] // dependencies
)

// Use computed value
const displayName = fullNameComputed() // Cached result

Element State Binding

// Bind element content to state
const userDisplay = div()
  .bindState(appState, 'user', 'textContent', 
    (user) => `Welcome, ${user.name}!`
  )

// Bind element attributes
const themeDiv = div()
  .bindState(appState, 'theme', 'class')

// Reactive element content
const counter = p()
  .bindComputed(appState, (state) => `Count: ${state.count}`)

// Make entire element reactive
const dynamicContent = div()
  .reactive(appState, (state) => [
    h2(state.title),
    p(state.description),
    state.items.map(item => li(item.name))
  ])

📋 Form Management

import { createFormState } from 'micro-js-html'

// Create form with validation
const formState = createFormState(
  // Initial values
  { 
    email: '', 
    password: '',
    confirmPassword: '' 
  },
  // Validators
  {
    email: (value) => {
      if (!value) return 'Email is required'
      if (!value.includes('@')) return 'Invalid email format'
      return null
    },
    password: (value) => {
      if (!value) return 'Password is required'
      if (value.length < 8) return 'Password must be at least 8 characters'
      return null
    },
    confirmPassword: (value, allValues) => {
      if (value !== allValues.password) return 'Passwords do not match'
      return null
    }
  }
)

// Handle form interactions
formState.setValue('email', '[email protected]')
formState.setTouched('email')

// Check validation
const isValid = formState.get('isValid')
const errors = formState.get('errors')
const values = formState.get('values')

// Create reactive form
const loginForm = form({ 
  onsubmit: (e) => {
    e.preventDefault()
    if (formState.validateAll()) {
      console.log('Form is valid:', formState.get('values'))
    }
  }
},
  input({ 
    type: 'email',
    value: formState.get('values').email,
    onchange: (e) => formState.setValue('email', e.target.value),
    onblur: () => formState.setTouched('email')
  }),
  formState.get('errors').email && 
    p({ class: 'error' }, formState.get('errors').email),
  
  button({ type: 'submit' }, 'Login')
)

🎨 Advanced Rendering

Render Helper

import { createRenderHelper } from 'micro-js-html'

// Create advanced renderer
const renderer = createRenderHelper('#app', {
  debounceMs: 16,        // Debounce renders for performance
  enableVirtualDOM: true, // Basic virtual DOM comparison
  onError: console.error  // Custom error handling
})

// Render with state binding
renderer.render((stateData) => 
  div(
    h1(stateData.title),
    p(`Users: ${stateData.users.length}`),
    ul(stateData.users.map(user => 
      li(`${user.name} - ${user.email}`)
    ))
  ),
  appState
)

// Cleanup when done
renderer.destroy()

Reactive Components

import { createReactiveComponent } from 'micro-js-html'

// Create self-updating component
const TodoApp = createReactiveComponent(
  todoState,
  (state) => div(
    h1('Todo App'),
    input({ 
      placeholder: 'Add todo...',
      onkeypress: (e) => {
        if (e.key === 'Enter') {
          todoState.set('todos', [
            ...todoState.get('todos'),
            { id: Date.now(), text: e.target.value, done: false }
          ])
          e.target.value = ''
        }
      }
    }),
    ul(
      state.todos.map(todo => 
        li({ 
          class: todo.done ? 'completed' : '',
          onclick: () => toggleTodo(todo.id)
        }, todo.text)
      )
    )
  ),
  '#todo-container'
)

// Mount component
TodoApp.mount()

// Unmount when done
TodoApp.unmount()

🌐 Server-Side Usage

// server.js
import { htmlTags } from 'micro-js-html'
import http from 'http'

const { html, head, body, title, h1, p } = htmlTags

function requestHandler(req, res) {
  const page = html(
    head(title('My Server App')),
    body(
      h1('Welcome to Server-Side Rendering'),
      p('This HTML was generated on the server!')
    )
  )

  res.writeHead(200, { 'Content-Type': 'text/html' })
  res.end(page.render())
}

http.createServer(requestHandler).listen(3000)
console.log('Server running at http://localhost:3000')

🔧 API Reference

Core Functions

htmlTags

Object containing all HTML element functions.

import { htmlTags } from 'micro-js-html'
const { div, p, h1 } = htmlTags

createState(initialState)

Creates a reactive state object.

Parameters:

  • initialState (Object): Initial state values

Returns: State object with methods:

  • get(key) - Get a value
  • set(key, value) - Set a value and notify watchers
  • getAll() - Get all state
  • update(updates) - Batch update multiple values
  • watch(callback) - Watch for changes
  • notify() - Manually trigger notifications
  • computed(key, computeFn, deps) - Create computed value
  • batch(updateFn) - Batch multiple updates

createFormState(initialValues, validators)

Creates a form state manager with validation.

Parameters:

  • initialValues (Object): Initial form values
  • validators (Object): Validation functions

Returns: Form state object with additional methods:

  • setValue(field, value) - Set field value
  • setTouched(field) - Mark field as touched
  • validateField(field, value) - Validate single field
  • validateAll() - Validate all fields
  • reset() - Reset form to initial state

createRenderHelper(container, options)

Creates an advanced render helper with performance optimizations.

Parameters:

  • container (string|Element): DOM selector or element
  • options (Object): Configuration options
    • debounceMs (number): Debounce delay in milliseconds
    • enableVirtualDOM (boolean): Enable virtual DOM comparison
    • onError (function): Error handler

createReactiveComponent(state, renderFn, container)

Creates a reactive component that auto-updates with state changes.

Parameters:

  • state (Object): State object from createState()
  • renderFn (function): Function that returns elements
  • container (string|Element): DOM container

Element Methods

All HTML elements support these methods:

bindState(state, stateKey, targetProp, transform)

Bind element to state property.

bindComputed(state, computeFn)

Bind element to computed state value.

reactive(state, renderFn)

Make element reactive to state changes.

unbindState()

Clean up all state bindings.

🎯 Best Practices

Performance

// Use batching for multiple updates
state.batch((s) => {
  s.set('loading', true)
  s.set('error', null)
  s.set('data', newData)
})

// Use computed values for expensive calculations
const expensiveComputed = state.computed('result',
  (state) => heavyCalculation(state.data),
  ['data']
)

// Debounce renders for better performance
const renderer = createRenderHelper('#app', { debounceMs: 16 })

State Organization

// Organize state by feature
const appState = createState({
  ui: { theme: 'light', sidebar: false },
  user: { name: '', email: '', preferences: {} },
  data: { items: [], loading: false, error: null }
})

// Use derived state for computed values
const uiState = appState.derive('isDarkMode', 
  (state) => state.ui.theme === 'dark',
  ['ui']
)

Error Handling

// Always handle errors in watchers
state.watch((data) => {
  try {
    updateUI(data)
  } catch (error) {
    console.error('UI update failed:', error)
  }
})

// Use error boundaries in render helpers
const renderer = createRenderHelper('#app', {
  onError: (error) => {
    console.error('Render error:', error)
    // Show error UI
  }
})

🔮 Future Features

  • WebSocket Integration - Real-time state synchronization
  • Server-Sent Events - Live data streaming
  • Enhanced Virtual DOM - More sophisticated diffing
  • DevTools Integration - State debugging tools
  • TypeScript Definitions - Full type safety

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

📄 License

MIT License - see LICENSE file for details.

🙏 Acknowledgments

  • Built for the micro-js ecosystem
  • Inspired by modern reactive frameworks
  • Designed for simplicity and performance

micro-js-html - It's actually just JavaScript! 🚀