RepoPilotOpen in app →

jorgebucaran/hyperapp

1kB-ish JavaScript framework for building hypertext applications

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • 16 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • Stale — last commit 1y ago
  • Concentrated ownership — top contributor handles 61% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the “Healthy” badge

Paste into your README — live-updates from the latest cached analysis.

RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/jorgebucaran/hyperapp)](https://repopilot.app/r/jorgebucaran/hyperapp)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/jorgebucaran/hyperapp on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: jorgebucaran/hyperapp

Generated by RepoPilot · 2026-05-06 · Source

Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/jorgebucaran/hyperapp shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

Verdict

GO — Healthy across all four use cases

  • 16 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Stale — last commit 1y ago
  • ⚠ Concentrated ownership — top contributor handles 61% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live jorgebucaran/hyperapp repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/jorgebucaran/hyperapp.

What it runs against: a local clone of jorgebucaran/hyperapp — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in jorgebucaran/hyperapp | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 442 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>jorgebucaran/hyperapp</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of jorgebucaran/hyperapp. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/jorgebucaran/hyperapp.git
#   cd hyperapp
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of jorgebucaran/hyperapp and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "jorgebucaran/hyperapp(\\.git)?\\b" \\
  && ok "origin remote is jorgebucaran/hyperapp" \\
  || miss "origin remote is not jorgebucaran/hyperapp (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "index.js" \\
  && ok "index.js" \\
  || miss "missing critical file: index.js"
test -f "index.d.ts" \\
  && ok "index.d.ts" \\
  || miss "missing critical file: index.d.ts"
test -f "packages/dom/index.js" \\
  && ok "packages/dom/index.js" \\
  || miss "missing critical file: packages/dom/index.js"
test -f "docs/architecture/state.md" \\
  && ok "docs/architecture/state.md" \\
  || miss "missing critical file: docs/architecture/state.md"
test -f "docs/architecture/actions.md" \\
  && ok "docs/architecture/actions.md" \\
  || miss "missing critical file: docs/architecture/actions.md"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 442 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~412d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/jorgebucaran/hyperapp"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Hyperapp is a 1kB virtual DOM framework for building interactive web applications using pure JavaScript and functional programming patterns. It combines state management, declarative views, effects, and subscriptions into a minimal API where UI = f(state), and user interactions trigger actions that update state deterministically. Monorepo structure: core framework is in index.js and index.d.ts at root; packages/ contains optional domain-specific modules (packages/dom, packages/html, packages/svg for rendering; packages/events and packages/time for side effects). Comprehensive docs in docs/api/ explain h(), text(), app() API; docs/architecture/ explains Views, Actions, Effects, Subscriptions, State, and Dispatch patterns.

Who it's for

JavaScript developers building single-page applications who want a declarative, functional alternative to React or Vue without the bundle size overhead. Particularly appeals to developers who value minimalism, understand functional composition, and need predictable state flow in small-to-medium complexity apps.

Maturity & risk

Production-ready and stable. Version 2.0.22 indicates mature semver discipline. The repo shows active CI/CD pipeline (.github/workflows/ci.yml), comprehensive test coverage (tests/index.test.js with c8), and full TypeScript definitions (index.d.ts). However, commit recency and open issue count are not visible from provided data.

Low-to-moderate risk: single maintainer (Jorge Bucaran) is a concentration point. No external dependencies listed in package.json (only dev tools c8 and twist), which eliminates supply chain risk but creates maintenance burden. Framework maturity (v2.0.x) means breaking changes are unlikely. Main risk is adoption volatility in a React/Vue-dominated ecosystem.

Active areas of work

Unable to determine from provided data. Repository structure shows stable v2 codebase with defined package boundaries. Check GitHub Issues and recent commits for current development focus.

Get running

git clone https://github.com/jorgebucaran/hyperapp.git && cd hyperapp && npm install && npm test

Daily commands: npm test (runs c8 twist tests/index.test.js). No dev server defined; examples run directly in browser via <script type="module"> import from CDN or local index.js.

Map of the codebase

  • index.js — Core framework entry point exporting h, text, app, memo—every contributor must understand the main API surface and how state/actions flow
  • index.d.ts — TypeScript definitions that document the public API contract and types for all core abstractions
  • packages/dom/index.js — Virtual DOM diffing and patching engine—critical for rendering performance and correctness of view updates
  • docs/architecture/state.md — Explains immutable state model and how state flows through actions—foundational to understanding Hyperapp's data architecture
  • docs/architecture/actions.md — Documents action dispatch mechanism and integration with effects/subscriptions—essential for understanding app control flow
  • docs/api/app.md — API documentation for app() initialization function that bootstraps the entire framework instance
  • tests/index.test.js — Comprehensive test suite validating core behavior of h, app, subscriptions, and effects—reference for expected behavior

Components & responsibilities

  • app() (Core index.js; DOM API; closure-based state storage) — Initializes framework: bootstraps state, mounts view to DOM, wires action dispatch and effect/subscription runners
    • Failure mode: Invalid init, view, or node config causes silent failure or crash; missing effect runner breaks side-effect chain
  • h() / text() (Plain JavaScript objects and arrays) — Creates virtual node objects representing UI structure; no DOM operations, purely data structure creation
    • Failure mode: Invalid props or children cause rendering errors downstream; no validation in h() itself
  • DOM Diff & Patch Engine (packages/dom/index.js) (DOM API; tree traversal and comparison algorithms) — Compares old and new virtual trees; determines minimal DOM mutations; applies patches to real DOM
    • Failure mode: Incorrect diff logic causes stale or corrupted DOM; missed patch operations leave UI out of sync with state
  • Action & State Update (Immutable object spread; pure functions) — User code: accepts action function that computes new state; framework dispatches and triggers re-render
    • Failure mode: Mutations in action function break assumptions; returned non-state causes rendering failures
  • Effect & Subscription Runners — Executes side-effects (timers, events, API calls) after state

How to make changes

Add a new Effect Type

  1. Create a new package under packages/{effectName}/ following the pattern of packages/time/ or packages/events/ (packages/{effectName}/index.js)
  2. Export a function that returns an effect object {props: {...}, handler: (dispatch, props) => {...}} compatible with app's effect runner (packages/{effectName}/index.js)
  3. Document the new effect in packages/{effectName}/README.md with usage examples (packages/{effectName}/README.md)
  4. Add package.json with name, version, description, and main entry point (packages/{effectName}/package.json)

Create a New Application

  1. Import h, text, and app from index.js at the root of hyperapp or via npm module (index.js)
  2. Define initial state object and view function that returns h() virtual nodes (your-app.js)
  3. Define action functions that take (state, props) and return new state using immutable patterns (your-app.js)
  4. Call app({init, view, node, subscriptions, dispatch}) to bootstrap and mount the app to a DOM element (your-app.js)
  5. Optionally use packages/events, packages/time, or other effect modules for side-effects (your-app.js)

Extend Core Virtual DOM Behavior

  1. Read and understand the diff algorithm and patch logic in packages/dom/index.js (packages/dom/index.js)
  2. Modify node creation, property patching, or event listener attachment in the VirtualDOM reconciliation loop (packages/dom/index.js)
  3. Add or update tests in tests/index.test.js to verify the new DOM behavior does not regress (tests/index.test.js)

Why these technologies

  • Virtual DOM with diffing algorithm — Decouples application logic from DOM mutations; enables declarative views and minimizes direct DOM manipulation for performance
  • Pure functions for views and actions — Deterministic rendering and state updates make behavior predictable, testable, and easier to reason about
  • Immutable state updates — Eliminates side-effect bugs and enables efficient change detection; simplifies time-travel debugging and state replay
  • Effects system with dependency-style runners — Isolates side-effects from pure state/view logic; allows composable, reusable effect libraries (time, events, http)
  • Subscriptions for external events — Clean API for wiring external event sources (WebSocket, timer, localStorage) into the action dispatch loop without side-effect boilerplate

Trade-offs already made

  • Monolithic 1kB framework with all core features in index.js rather than pluggable micro-modules

    • Why: Minimizes bundle size and dependencies for the happy path; most apps only need core h(), app(), and view/state logic
    • Consequence: Advanced features (subscriptions, effects, memo) are less modular; harder to tree-shake unused code if only building simple UIs
  • Virtual DOM diffing instead of fine-grained reactivity (like Solid.js or Vue 3's Proxy)

    • Why: Simpler mental model and API; no need to track signals or proxies; works well at small to medium scale
    • Consequence: Slightly larger re-render overhead for very large or frequently-updating component trees compared to fine-grained reactivity
  • Functional action/state model instead of mutable object mutation or immer-style drafts

    • Why: Explicit, pure, and minimal magic; easy to understand and test
    • Consequence: Verbose object spread syntax for deep state updates; no automatic batching; developer must handle immutability correctly
  • No built-in router or form library

    • Why: Keeps core size tiny and lets community build opinionated tools on top
    • Consequence: Apps must choose or build their own routing and form state management; more boilerplate for typical web applications

Non-goals (don't propose these)

  • Real-time synchronization or CRDT support—not a database framework
  • Server-side rendering (SSR) or static generation—client-side UI focus only
  • CSS-in-JS or style encapsulation—delegated to external CSS/styling solutions
  • Form validation or HTTP client abstractions—minimal opinions on side-effect handling
  • Backward compatibility with Hyperapp v1—clean break to minimize v2 bundle size

Traps & gotchas

No build step is both a feature and a gotcha: code must be valid ES modules; CommonJS require() won't work. The 1kB size constraint means some convenience features from larger frameworks are absent (e.g., no built-in form helpers, no routing). Virtual DOM diffs are exact JavaScript object comparisons; mutating nested state directly will cause missed re-renders—always return new state objects. Subscriptions require manual cleanup to prevent memory leaks.

Architecture

Concepts to learn

  • Virtual DOM (VDOM) — Core abstraction in Hyperapp: h() creates lightweight JS objects representing DOM; framework diffs and patches real DOM incrementally rather than replacing it wholesale
  • Unidirectional Data Flow — Hyperapp enforces state → view → action → state cycle; eliminates two-way binding confusion and makes state changes predictable and debuggable
  • Effects (side effects in pure FP) — Hyperapp isolates DOM mutations, HTTP calls, localStorage access into explicit Effect objects returned from actions; keeps core logic pure and testable
  • Subscriptions (event listeners) — Hyperapp's mechanism for listening to external events (keyboard input, timers, WebSocket messages) and feeding them back into the action → state cycle
  • Functional Composition (action factories) — Hyperapp actions are pure functions (state, payload) => newState; patterns like currying and higher-order functions compose actions into reusable state transformations
  • Shallow Diffing (VDOM reconciliation) — Hyperapp's diff algorithm compares VDOM trees node-by-node to detect only what changed; the hand-optimized version explains why it's 1kB instead of 10kB like other frameworks

Related repos

  • preactjs/preact — Another ultra-lightweight VDOM framework (3kB); similar goals but larger API surface and more optimizations
  • infernojs/inferno — Minimal VDOM framework competing on size and performance; uses same h() VDOM signature borrowed from Hyperapp's design
  • jorgebucaran/hyperawesome — Official curated collection of Hyperapp examples, third-party packages, and real-world application templates
  • facebook/react — The dominant VDOM framework; understanding React's reconciliation helps contrast Hyperapp's simpler diff algorithm
  • frenziedelectric/hyperapp-todolist-example — Community example; demonstrates realistic Hyperapp patterns beyond the docs README example

PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive test coverage for packages/{dom,events,html,svg,time}/index.js

The repo has a single tests/index.test.js but the file structure shows 5 packages (dom, events, html, svg, time) with their own index.js implementations. Each package has a README but no corresponding test files. New contributors can add dedicated test suites for each package to ensure their effects, event handlers, and DOM utilities work correctly in isolation. This directly improves code reliability and makes future refactoring safer.

  • [ ] Create tests/packages/dom.test.js with tests for DOM utilities from packages/dom/index.js
  • [ ] Create tests/packages/events.test.js with tests for event binding from packages/events/index.js
  • [ ] Create tests/packages/html.test.js with tests for HTML element creation from packages/html/index.js
  • [ ] Create tests/packages/svg.test.js with tests for SVG utilities from packages/svg/index.js
  • [ ] Create tests/packages/time.test.js with tests for time/subscription utilities from packages/time/index.js
  • [ ] Update test script in package.json if needed to include new test files

Add TypeScript-specific tests and validation to CI workflow

The repo has index.d.ts for TypeScript type definitions but .github/workflows/ci.yml likely only runs JavaScript tests. A new contributor can add type checking validation in the CI pipeline using tsc or similar tools to ensure the TypeScript definitions match the actual implementation and catch type regressions. This prevents type definition drift and improves DX for TypeScript users.

  • [ ] Examine current .github/workflows/ci.yml to understand existing test steps
  • [ ] Add typescript as a devDependency in package.json
  • [ ] Create a tsconfig.json with strict mode enabled for type validation
  • [ ] Add a CI step to run tsc --noEmit to validate index.d.ts against index.js
  • [ ] Document in README or CONTRIBUTING.md that type definitions must be kept in sync

Add integration tests for app lifecycle (init, subscription, effect dispatch cycle)

The architecture docs reference state, actions, effects, subscriptions, and dispatch, but tests/index.test.js may lack comprehensive integration tests covering the full lifecycle. New contributors can add tests that verify the complete app initialization flow with initial state, effects triggering on mount, subscriptions firing, and nested action dispatches. This ensures the core framework contract works correctly across documented behaviors in docs/architecture/.

  • [ ] Review docs/architecture/state.md, effects.md, subscriptions.md, and dispatch.md
  • [ ] Add test case: app initializes with correct initial state from index.test.js
  • [ ] Add test case: effects execute on app mount and dispatch actions
  • [ ] Add test case: subscriptions are registered and triggered correctly
  • [ ] Add test case: nested action dispatches resolve in correct order
  • [ ] Add test case: view re-renders when state changes via actions

Good first issues

  • Add integration tests for packages/events/index.js in tests/index.test.js to validate event delegation patterns with nested event handlers and bubbling behavior
  • Expand docs/tutorial.md with a multi-step example showing AddTodo → EditTodo → DeleteTodo actions chained together to demonstrate action composition
  • Create example in docs/ showing how to use packages/time/index.js for setInterval subscriptions (e.g., updating a clock or polling an API)

Top contributors

Recent commits

  • 5a113fa — Home is where the git isn't (jorgebucaran)
  • a039d63 — Fix link to actions.md (#1121) (ppzreboot)
  • f185df6 — Should be a list (jorgebucaran)
  • 6b116ed — Fix c8 codecov upstream issue for the time being (jorgebucaran)
  • f0ecd3d — Remove 12.x Node from workflow (jorgebucaran)
  • 7a7a92b — fix: Update tutorial.md (#1116) (bitsnaps)
  • c3717e3 — Enhance augmented dispatch examples (#1112) (kofifus)
  • 5dff723 — Typo (#1110) (zaceno)
  • 44aa0df — Improve example of returning array of nodes (#1109) (zaceno)
  • 9ee6a50 — Use examples that work (#1107) (zaceno)

Security observations

Hyperapp's security posture is generally solid for a minimalist framework. The codebase itself shows no critical vulnerabilities, hardcoded secrets, or obvious injection vectors. However, there are maintenance-related concerns around dependency management (unversioned dev dependencies) and packaging configuration (overly broad file inclusion). The project would benefit from stricter dependency pinning, security documentation, and automated security scanning in CI/CD. As a frontend framework, users should be educated on secure usage patterns to prevent XSS when handling untrusted input.

  • Medium · Wildcard File Inclusion in Package.json — package.json - files field. The 'files' field in package.json uses '.[tj]s' which includes all TypeScript and JavaScript files in the root directory and all subdirectories. This could potentially include unintended files if the repository structure changes or if sensitive files are added without careful naming conventions. Fix: Use explicit file inclusion patterns instead of wildcards. Specify exact paths like ['index.js', 'index.d.ts', 'packages//index.js'] to have precise control over published files.
  • Medium · Unversioned Development Dependencies — package.json - devDependencies. Development dependencies (twist, c8) use '*' as the version, meaning they install the latest version without pinning. This creates non-deterministic builds and increases the risk of unexpected behavior changes from upstream dependencies. Fix: Pin specific versions for all dependencies using exact version strings (e.g., '^1.2.3' or '1.2.3') instead of wildcards. Use lockfiles (package-lock.json or yarn.lock) to ensure reproducible builds.
  • Low · Missing npm Package Audit Configuration — package.json and repository root. No evidence of npm audit configuration, npm scripts for security scanning, or security policy documentation (.github/SECURITY.md) in the visible file structure. Fix: Add 'npm audit' to CI/CD pipeline, create a SECURITY.md file with vulnerability reporting guidelines, and configure dependabot or Snyk for automated dependency scanning.
  • Low · No Content Security Policy or Security Headers Documentation — docs/ directory and README.md. As a framework that generates HTML/DOM content, there is no visible documentation about XSS prevention or CSP best practices for users of this library. Fix: Add security guidelines to documentation explaining how to safely use the h() and text() functions to prevent XSS attacks, especially when handling user-generated content.

LLM-derived; treat as a starting point, not a security audit.

Where to read next


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · jorgebucaran/hyperapp — RepoPilot