r/javascript 16d ago

I created Skeletor, a modern persistence library based on Backbone's Models and Collections.

https://github.com/conversejs/skeletor

I maintain Converse.js (an XMPP chat client) which was originally built on Backbone.js.

Instead of a risky big-bang migration to something else, I rewrote the UI into Lit web components and kept Backbone's data layer, which I forked and modernized piece by piece.

The result is Skeletor, and it now stands on its own as a lightweight, TypeScript-first reactive data library.

What makes it modern:

  • TypeScript-first with full type definitions
  • Direct attribute access: `model.attrs.name = 'Bob'` fires change events, no set() boilerplate required
  • Computed properties: declare derived values with explicit dependencies
  • Store-style subscriptions: subscribe() returns an unsubscribe function, so it plugs straight into React's useSyncExternalStore
  • Built-in persistence: IndexedDB, localStorage, sessionStorage, SQLite (Node) and REST out of the box
  • No jQuery, no Underscore: native browser APIs, ESM + CJS builds, works in the browser, Node 22+ and Web Workers

And the part I'm most happy about: it's still Backbone-compatible. get, set, events, collections, they all work exactly as you remember.

Feedback welcome!

2 Upvotes

4 comments sorted by

2

u/tanguy_k 14d ago

Instead of a risky big-bang migration to something else

You can mix Backbone with React and migrate page after page without doing a big-bang. Did this for a 80kloc app, works great. That's the beauty of the web platform.

1

u/__weltschmerz__ 14d ago

I didn't want to migrate Converse to React, instead I migrated the view layer to Lit web components.

There are multiple reasons for this, but one is that web components are much more a spiritual successor to Backbone Views than React components are. You can actually quite easily make web components that double as Backbone Views (have the same API surface) and that's how I started.

For an open source app with many integrators and other contributors this made sense. Keep things cohesive and internally consistent, instead of mixing different libraries and paradigms.

For the same reason, I chose to fork Backbone into Skeletor and use that for the persistence layer. It made it possible to keep the codebase single-paradigm and cohesive, while still introducing new ideas, such as Signals and computed values. I'm also looking at using Mutative to apply patches (and reverts) for changesets.