hyperapp icon indicating copy to clipboard operation
hyperapp copied to clipboard

Future of hyperapp

Open coderkini opened this issue 5 months ago • 10 comments

Hello, I wanted to understand what the development roadmap for hyperapp is? Given that the last release was way back in 2022, I wanted to know if this project is being actively maintained and there is a future for the same. I really love this light-weight framework and would like to use it on a project I am about to start, but the lack of recent releases is keeping me on the backfoot.

I request if one of the maintainers could please throw some light on the plans for this framework and what the release cycle would look like. If this question has been answered before, I would request someone to please point me to the same. Thanks in advance!

coderkini avatar Jul 15 '25 04:07 coderkini

I'm not the owner but a long time contributor. I haven't heard from Jorge in quite a while, so just to not keep you waiting forever I'll leave my own 2c. The short of it is: there is no future roadmap, mainly because there is nothing left to change.

Hyperapp is pretty much "done" in the sense that it does precisely what it says and it does it well. There have been no (actual) bugs discovered since 2022, and there aren't any gaping holes in the api. This is kind of the nature of minimalist projects like this one. They're not trying to keep up with trends, and there isn't much that can go wrong, so once they're done they're done.

You never know of course, bugs & deprecated browser apis still might happen. If so I'm certain Jorge or someone will step in and take care of it. But the nice thing about such a tiny project like hyperapp is that you arent reliant on maintainers keeping up - you can relatively easily deal with any issues yourself. Sure the code isn't exactly written with readability in mind but it's only 400 lines in a single file. An afternoon with a debugger and you're set.

In my estimation, this makes hyperapp a truly low-risk option for any project. You have control of the code not only in the theoretical, legal sense, but also in the actual practical sense.

To be transparent about the downsides:

The subscriptions api is not great. I mean it works - no bugs! - but there are a fair bit of pitfalls/caveats you might not be expecting. If there was an area of hyperapp I'd say needs work, it would be this.

Also, while the docs are fine, hyperapp may need you to step out of your familiar patterns, and it's not always obvious what the "right" way to approach a problem is. And unfortunately the community both here and on discord is not exactly lively.

The other thing people kind of find lacking is ecosystem. I think Hyperapp mostly appeals to minimalists, so they aren't exactly out looking for what more kinds of libraries they can drop in to their project, preferring to get away with as little complexity and bloat as possible. Even so, it would be nice if there were more standard solutions to common problems.

Shameless plug section:

To help a bit with "thinking in Hyperapp" I made a video tutorial series you might find interesting: https://zaceno.github.io/codealong-hyperapp/

To bring some modern (and frankly, expected) tooling to hyperapp I made a vite plugin https://github.com/zaceno/vite-plugin-hyperapp - makes it easy to get started with a dev setup with JSX, HMR, SSR - all the buzzwords :) .

I also made an astro-plugin for Hyperapp: https://github.com/zaceno/zx-astrojs-hyperapp . Astro is kind of like Next.js (a server-side framework) but agnostic about which (if any) frontend library you use. And I like it a lot better than Next.js. This plugin has some improvements coming shortly.

zaceno avatar Jul 17 '25 23:07 zaceno

Hello,

I am not an active member of the community, but I've been using Hyperapp for several projects over the last couple of years. I second what @zaceno just said - I also consider Hyperapp to be "done". It has a limited scope, which it performs really well. If you love it, then yes, you should definitely use it. It is a bit mind-bending, though. The concepts are clearly defined but I struggle writing functions that return other functions, coming from an OOP background.

I would add that its minimal footprint and model handling make it suitable for a scenario that made me try it in the first place: reclaim server-side development. It allows you to write your app as "mostly server-side", and just drop Hyperapp in wherever needed, including inside web components. For me, this changes everything. I can keep writing apps in "established" server-side technologies (in my case, C#), and generate scripts on the flight that just call into Hyperapp.

In my opinion, the future of Hyperapp lies in the use cases where it's the only game in town. The minimalism is not just architecturally beautiful, it also has very practical considerations.

calin-dobos avatar Jul 18 '25 10:07 calin-dobos

Hi!

I'm chiming in quite late. But I have been very busy—for quite some years, and on a daily basis, one can say!—developing all sorts of Hyperapp applications, both big and small, both client-side and server-side rendered, in both light and shadow DOM, both as standalone, independent apps and interoperable, collaborative apps. And I've been doing this not only for a huge intranet—including a web-components based design system—of a major German company, but also for the charming website of a small, family-run German publisher (The Little Prince, Peter Rabbit, Winnie the Pooh, ...), and some other, smaller projects.

I mean, for this matter Hyperapp is stress tested in all kinds of ways. And it has proven to be rock solid! And very fast. And oh so tiny! I was never truly disappointed. I love its sheer simplicity, its clear architectural concepts, its elegancy in terms of API surface area, its very catchy mind model.

Sure, as @zaceno mentioned, subscriptions have their pitfalls. But knowing them (they are documented!) has helped me setting them up correctly. And adding event listeners can only be done without additional options. One might also miss refs to hold a reference to DOM nodes that are rendered in the view. But for these there are easy alternatives.

Hyperapp is your minimalist butler, undemanding and unpretentious, always there when you need it, quick to respond, serving you reliably, and accompanying you for a life time. The good thing is, that you don't have to worry about learning a new language every time you have acquired a fancy new, but helplessly overweight work horse, and need to explain your needs to it again. I mean, who speaks horse language fluently anyways?

There's one thing I am missing, though: a proper website like all the other libs and frameworks have. To better promote Hyperapp. To spread the gospel and make this React-ridden world a better one again. It may contain the same docs and tutorial as on Github. Just a little nicer than the "raw" markdown renderings. Too bad the hyperapp.dev domain is gone now, leading to nirvana. But in the end, the missing website only adds to Hyperapp's minimalism. Which makes it so sexy for me...

Hyperapp isn't dead, long live Hyperapp!

skanne avatar Aug 09 '25 21:08 skanne

@skanne is right - inability to set options on event listeners is a genuinely a missing feature, and particularly for events that should be marked as passive, I haven't found any good workaround. In such cases I have resorted to the following prototype override. But overriding prototypes is generally considered a no-no, so if someone (@skanne ?) has a better suggestion I'd love to hear it!

/*
Overrides the addEventListener and removeEventListener
on HTMLElement.prototype in order to enable passive
event listeners in hyperapp, by adding the suffix
"_passive" to an "onsomevent" property. I.e:

h("div", {ontouchstart_passive: SomeAction}, ...)

SomeAction will be bound to the touchstart event,
but using {passive: true} option passed to 
addEventListener.

*/
const originalAddEventListener = HTMLElement.prototype.addEventListener
const originalRemoveEventListener = HTMLElement.prototype.removeEventListener
HTMLElement.prototype.addEventListener = function (name, handler, options) {
  if (name.match(/_passive$/)) {
    const trueName = name.replace(/_passive$/, "")
    this.events[trueName] = this.events[name]
    return originalAddEventListener.call(this, trueName, handler, {
      passive: true,
    })
  }
  return originalAddEventListener.call(this, name, handler, options)
}
HTMLElement.prototype.removeEventListener = function (name, handler, options) {
  if (name.match(/_passive$/)) {
    const trueName = name.replace(/_passive$/, "")
    delete this.events[trueName]
    name = trueName
  }
  return originalRemoveEventListener.call(this, name, handler, options)
}

zaceno avatar Aug 13 '25 11:08 zaceno

More or less exactly one year ago I did come up with a solution, but I never went public with it. The solution would require a pull request, though.

Patch

To add support for event listener options "natively" in Hyperapp, the patchProperty function could be enhanced where the event listeners are added and removed:

var patchProperty = (node, key, oldValue, newValue, listener, isSvg) => {
  if (key === "style") {
    for (var k in { ...oldValue, ...newValue }) {
      oldValue = newValue == null || newValue[k] == null ? "" : newValue[k]
      if (k[0] === "-") {
        node[key].setProperty(k, oldValue)
      } else {
        node[key][k] = oldValue
      }
    }
  } else if (key[0] === "o" && key[1] === "n") {
    if (
      !((node.events || (node.events = {}))[(key = key.slice(2))] = newValue)
    ) {
      node.removeEventListener(key, listener, newValue[_options])   // <-- here
    } else if (!oldValue) {
      node.addEventListener(key, listener, newValue[_options])      // <-- and here
    }
  } else if (!isSvg && key !== "list" && key !== "form" && key in node) {
    node[key] = newValue == null ? "" : newValue
  } else if (newValue == null || newValue === false) {
    node.removeAttribute(key)
  } else {
    node.setAttribute(key, newValue)
  }
}

_options is a Hyperapp-internal variable holding a symbol:

var _options = Symbol()

Then we'd need another function withOptions that is exported together with h, text, app, and memo:

export var withOptions = (action, options) => (action[_options] = options, action)

Usage

In user land it'd look like this:

import { h, text, app, withOptions } from 'hyperapp'

const Once = state => ({ ...state, done: true })

app({
  init: {},
  node: document.body.firstChild,
  view: state =>
    h(
      'button',
      {
        onmousedown: withOptions(Once, { once: true }),
        ontouchstart: withOptions(Once, { once: true, passive: true }),
      },
      text(state.done ? "Sorry, no more clicking or touching" : "Click or touch me")
    ),
})

Caveat

In the above example, the Once action is augmented with options twice, with the latter/last one prevailing! To avoid this, wrap the action in a tuple/array:

      {
        onmousedown: withOptions([Once], { once: true }),
        ontouchstart: withOptions([Once], { once: true, passive: true }),
      },

Could this work for us? I'd prepare a PR.

skanne avatar Aug 13 '25 18:08 skanne

Another option, one not requiring a PR, is to employ @zaceno's "useRef-hook for Hyperapp" (with my tiny enhancement using a WeakSet for the guard).

import { h, text, app } from 'hyperapp'

const _seen = new WeakSet()

const withElement = (vnode, fn) => {
  let element
  Object.defineProperty(vnode, 'node', {
    get () { return element },
    set (e) {
      element = e
      if (!_seen.has(element)) {
        fn(element)
        _seen.add(element)
      }
    }
  })
  return vnode
}

const Once = state => ({ ...state, done: true })

const dispatch = app({
  init: {},
  node: document.body.firstChild,
  view: state =>
    withElement(
      h(
        'button',
        {},
        text(state.done ? "Sorry, no more clicking or touching" : "Click or touch me")
      ),
      button => {
        button.addEventListener('mousedown', () => dispatch(Once), { once: true })
        button.addEventListener('touchstart', () => dispatch(Once), { once: true, passive: true })
      }
    ),
})

Run the demo

skanne avatar Aug 13 '25 18:08 skanne

@skanne that solution of yours looks pretty darn clean to me. I say put the PR together and lets see where it goes!

It's a little bit of a shame that we need to rely on a "secret" symbol there. It feels a little... I don't know quite how to put it, but "specific". Like maybe there's another solution (when we're changing things anyway) that could be more general purpose. I don't know it's just a hunch. In any case your PR will be a good starting point for discussion.

The trick with withElement there is pretty neat - for some reason I never considered it. withElement is kind of a dirty hack 😅 - but also underscores that it would be nice to have a supported way to get more deep into the patching process.

zaceno avatar Aug 19 '25 20:08 zaceno

Since I mentioned in a previous comment, my hyperapp-astro integration, I wanted to let you guys know: it's been updated! I'm sure there are improvements to be made but feature-wise I'm calling it complete. Supports astros-hydration, server-props, server content slots, hot-module reloading, jsx and tsx - and also as a bonus: synchronized hyperapp "islands" (separate app instances on a page)

In honor of this feature completeness the npm package is now simpply 'astrojs-hyperapp', and the git repo was renamed: https://github.com/zaceno/astrojs-hyperapp.

Sorry for the self-plug - this just seemed like a good place to mention it :) Have a great day!

zaceno avatar Aug 22 '25 20:08 zaceno

Sorry for chiming in so late. There's no roadmap for Hyperapp (core) right now. I'm not saying it's perfect, but like it was noted, there aren't any major issues. That said, we could revisit the diff algorithm to make single passes faster by actually implementing the longest increasing subsequence after the bound checks here. We could also look at making animations easier by exposing some API, though that's super opinionated, and I don't remember us ever talking about it (or if we did, I forgot). Also, it's true that a way to set options on event listeners could be another thing to look at (if it can be done elegantly or at least pleasantly).

Outside core there's plenty left undone, like packages. We could do @hyperapp/http, @hyperapp/navigation, @hyperapp/random, and a few others. I just hit a reality wall with the http package. It just cannot be done in a pure Elm-or-Haskell-style way. It was naive to think that could be achieved. It is mostly a language limitation, and since I couldn't figure out a way forward, I basically stopped for a while and got stuck in a while true.

One path forward might be to push more for community or user-made packages. Nothing's stopping anyone from making their own, since the effect/sub API is fully exposed, but we could further encourage it. We could also keep @hyperapp/* simpler, like just the basic stuff: @hyperapp/html, etc., (probably the most pragmatic approach at this point).

Things like EffectTS give me hope we could eventually revisit some of those ideas in Hyperapp, maybe rewrite it in TypeScript too. That would definitely be a born-again kind of thing, since part of Hyperapp's original ethos was its 1 KB-ishness and the code being moderately golfed (not minified though, just handcrafted in a particularly obsessive style). Rewriting it in TypeScript would mostly lose that, but even if we did, it wouldn't invalidate what Hyperapp is. It would just evolve into something new.

I would add that its minimal footprint and model handling make it suitable for a scenario that made me try it in the first place: reclaim server-side development.

I love this comment because I used to be all about doing apps 100% on the frontend, no servers. But Hyperapp core doesn't care either way, so the fact that I used to think that way doesn't affect anyone using it with a totally different philosophy than what I had back then. So, Hyperapp, despite its purist, prescriptive tendencies, remains fundamentally agnostic in many respects.

@zaceno That's a veritable feast of impressive Hyperapp packages you've put together! 🤯

jorgebucaran avatar Oct 03 '25 12:10 jorgebucaran

@jorgebucaran Good to hear from you again, man! :)

Things like EffectTS give me hope we could eventually revisit some of those ideas in Hyperapp

Hadn't heard of EffectTS before - that looks pretty awesome actually. Even without a full rewrite of Hyperapp in TS, I'm thinking a middleware/dispatch-augmentation could enable using EffectTS effects and effect-pipelines in current Hyperapp. 🤔 Seems worth investigating.

zaceno avatar Oct 03 '25 13:10 zaceno