Framework
finom/matreshka avatar
finom/matreshka

Matreshka.js: two-way data binding for the HTML you already have

An old-school framework, maintained since 2013: two-way data binding for the HTML you already have.

892 stars66 forksJavaScriptMIT

At a glance

What is it?
Matreshka.js is an MIT licensed JavaScript framework for single-page applications that binds DOM state to plain JavaScript object properties with no build step and no dependencies, maintained since 2013 and released as 3.0 in October 2026. It suits a team that wants reactivity without adopting a component model, or that needs to add interactivity to existing markup rather than rewrite it.
Who is it for?
Matreshka.js fits a team that wants reactive DOM behaviour on existing markup, that prefers plain object properties to a component hierarchy, and that does not want a build step in its toolchain, since a script tag and a bindNode call are the whole setup.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two lines of code to bind a DOM value

Matreshka.js is a JavaScript framework to create single-page applications, and the API is based on JavaScript classes and accessors which are accessible to all application developers of varying skills. The stated requirement to start your first project with Matreshka.js is basic knowledge of JavaScript.

The example in the README is the whole demonstration:

html
<input class="in" type="text">
<output class="out"></output>

<script src="https://cdn.jsdelivr.net/npm/matreshka@3/dist/matreshka.min.js"></script>
<script>
const app = new Matreshka();
app.bindNode('x', '.in, .out');
app.x = 'A framework for single-page applications creation';
</script>

Two elements, a constructor call, a bindNode call with a property name and a CSS selector, and an assignment. The property x on the app object is the single source of truth, and both the input and the output are bound to it, so typing in the input updates x and assigning to x updates the output. That is two-way data binding, and it is what the project's description says it is: reactive data binding for the HTML you already have.

The design goal follows from that. One of the listed features is the ability to refactor legacy applications without rewriting them from scratch, which is a real constraint that most modern frameworks do not meet, because they assume you own the markup and the structure of the page.

The framework is zero dependencies, and the no-build claim is taken seriously enough to be in the package keywords, no-build. The example above loads a minified bundle from a CDN with a script tag and works. There is no compile step, no JSX, and no module graph to wire up.

That combination of a tiny setup and a small conceptual surface is the reason the README says only a couple of hours are needed to master the framework, because of the absence of complex concepts. Whether that is an asset or a limitation depends entirely on what you expected to get.

A 3.0 release after a decade, and what the version numbers say

The version history of this project tells two different stories, and both matter.

The recent releases are v3.0.1 on 2026-10-17, v3.0.0 on 2026-10-17, and v2.4.0 on 2017-04-14. The repository description says the framework is maintained since 2013. So this is a project that ran for years on a 2.x line, went quiet, and has just come back with a major version, in October 2026.

That gap is the most important thing to know before adopting it. The 2.4.0 release is from April 2017, and 3.0 followed in October 2026. Whatever you read in old tutorials, blog posts or answers about Matreshka.js describes the 2.x line, and the major version bump means the API is not guaranteed to be the same. The CHANGELOG.md at the repository root is the file to read, and it is linked from the README as the release history.

The maintenance picture is otherwise good. The repository is not archived, the last push was on 2026-10-22, five days after the 3.0 releases, and the default branch is main. The README carries badges for CI, coverage, the OpenSSF Scorecard and the OpenSSF Best Practices, which is a set of badges that usually reflects a project with a reasonably mature process around it, and the repository contains SECURITY.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md and GOVERNANCE.md.

There is a governance file, which is unusual for a project of this size and suggests the author is taking the institutional side of open source seriously rather than treating the repository as a personal scratchpad with a licence file.

So the picture is a long-lived, low-dependency, well-governed project that has just made a large version jump. If you are evaluating it now, you are evaluating the 3.0 line, and the 2.x tutorials are of historical interest only.

The package exports, and the two subpath add-ons

The package.json shows a deliberately small public surface, and the exports map is the clearest description of what the library actually offers.

The main entry is matreshka, with the root export pointing at ./dist/index.js and types at ./dist/index.d.ts. There is exactly one extra export alongside it, ./package.json, and then two subpath exports: ./router and ./parse-form. Both have their own types entries, and both are repeated in a typesVersions block so that older TypeScript resolution strategies find the same declaration files.

The two subpaths are worth pausing on, because they tell you what the framework considers out of scope for the core. A router and a form parser are two things that most frameworks of this size include in the main package, and here they are separate modules with their own import paths. A form parser in particular, something that turns a form element into a plain object, is a small self-contained utility that could reasonably live anywhere.

The distribution also targets the browser directly. The unpkg and jsdelivr fields both point at ./dist/matreshka.min.js, which is the same artefact the CDN script tag in the README example loads, and sideEffects is set to false, which lets a bundler drop unused exports when you import the package into a build.

The package is marked as type: module and as pure ESM, and the engines field requires node >=22.12. The files array ships only dist and CHANGELOG.md, and the workspaces field lists docs, so the documentation site is built from inside the same repository with the same tooling.

The framework itself is written in TypeScript. The repository has tsconfig.json and tsconfig.build.json, a vitest.config.mjs for the test run, and a biome.json for formatting and linting, and the examples directory contains fourteen separate example projects.

Fourteen examples, and a router that is a separate module

The examples directory is the clearest guide to what the framework can do, and the list of names is a good map of its surface.

There is hello-world and events, which is where you would start. There is contact-list, signup-form, parse-form, todomvc and product-table, which are the classic small application shapes. There is price-widget and markdown-editor, which are single-purpose widgets. There is treeview, which is a hierarchical view. There is matreshka-array and matreshka-object, which are named after the two data structures the binding works with, and are worth reading because they are the clearest statement of the core model. There is router, which is the example for the router subpath export.

The todomvc example is a useful calibration point, because that application is a standard test of whether a framework can handle list rendering, item state and events on a small scale. Its presence suggests the library can do that much, though the README does not make performance claims and the framework does not advertise a virtual DOM or a diffing algorithm, so the mechanism is more likely a targeted update of bound nodes than a general reconciler.

The treeview example is the interesting one for anyone with a hierarchical data problem, because a tree is a case where two-way binding gets hard fast. If the treeview example exists and is kept working, the framework can express a recursive structure without a component abstraction.

The examples are also how you should learn the library. A framework whose documentation is a single file for an LLM, llms-full.txt, is a signal about how the author expects the library to be consumed, and the website at matreshka.js.org carries the examples and tutorials. Note the split: the core, the router and the form parser are three separate import paths, so a project that needs none of the router and form parser carries only the core.

Installing it, with and without a package manager

There are three documented ways to get the library, and they differ in how much of your workflow they change.

The first is the script tag, shown in the example above, loading a minified build from jsDelivr at a versioned path. The README also links a direct download of matreshka.min.js from the project website for the no-CDN case.

The second is npm, and the command is:

bash
npm install --save matreshka

The third is new, and it is aimed at a specific workflow. The README has an Install the skill for AI coding agents section:

bash
npx skills add finom/matreshka

That installs an agent skill for coding assistants, which is a distribution channel that has appeared across open source projects in the last couple of years, and the repository layout supports it, with a skills/ directory at the top level. Whether you want your coding agent to know this framework's conventions is a choice, but the fact that it is offered suggests the author expects a meaningful share of the users to be working in that mode.

The other notable line in the README is a link to llms-full.txt, described as the whole documentation in one file for an LLM. Combined with the skill installer, that is a consistent position: the project is deliberately packaged for both humans reading the website and agents reading a text dump.

For a developer who wants the source and the tests, the repository also carries a scripts/ directory and a test/ directory, and the build is driven by the package.json scripts with TypeScript compiling to dist through tsconfig.build.json and Vitest running the tests.

One thing the README does not document is how to run the test suite or the build for a fork, since the scripts section of the package.json is cut off in the visible documentation. That is a small gap for a project offering a contributing guide.

What two-way binding costs you, and when to choose it

The honest limitations of this approach are worth stating plainly, because the feature list is written by someone who likes the design.

The first is the shape of the application. Two-way binding with plain object properties is excellent for pages that are mostly a form, a list, or a detail view. It is a poor fit for a large application with a deep component hierarchy, per-component state boundaries, typed props in both directions, and a need to reason about what re-renders when. Those are the problems modern component frameworks were built for, and this one deliberately does not solve them.

The second is that the binding is not observable in the way a component tree is. With plain properties, a debugger cannot always tell you which code changed a value and which subscribers reacted to it, and there is no virtual DOM diff to inspect. The README lists high reliability of developed apps among its features, and there is a reasoning path behind that claim, since a single source of truth removes a category of bug where the view and the model disagree. The same property is a cost when you want to trace an update.

The alternative approach, and the one most teams already have, is a component model with one-way data flow. In that world a parent owns the state, props flow down, and events flow up. It is more ceremony, and it is also easier to debug, to test, and to hand to another developer. The difference in approach is exactly this: Matreshka.js makes the property the boundary, a component framework makes the component the boundary.

A third practical note: the project is JavaScript-first. The README says the API is based on JavaScript classes and accessors accessible to all application developers of varying skills, and while the library is written in TypeScript and ships declarations, the design target is plain JavaScript objects rather than a typed component contract.

So the decision comes down to what you are building. A legacy page that needs to become interactive without a rewrite, or a small single-page tool, is exactly the case this library fits. A large product with dozens of screens and a team of developers is exactly the case where a component framework's ceremony pays for itself.

Editorial conclusion

Matreshka.js fits a team that wants reactive DOM behaviour on existing markup, that prefers plain object properties to a component hierarchy, and that does not want a build step in its toolchain, since a script tag and a bindNode call are the whole setup. It is the wrong choice if you have standardised on a component-based framework with typed props and a compiler, and the wrong choice if you need a router with nested layouts or form parsing at scale, since router and parse-form are separate subpath exports with their own scope. Before you adopt it, read ARCHITECTURE.md at the repository root, and note that version 3.0.0 and 3.0.1 both landed on 2026-10-17, so the major version is very new and the CHANGELOG.md is where the upgrade notes are.

Frequently asked questions

How do I bind a DOM element to a JavaScript property in Matreshka.js?

Create an instance with `new Matreshka()`, call `app.bindNode('x', '.in, .out')` with the property name and a CSS selector, and then assign to `app.x`. The README's example binds an input and an output to the same property for two-way updates.

Does Matreshka.js need a build step?

No. The README's example loads a minified build from a CDN with a script tag and works directly in the browser, and the package keywords include no-build. It is also installable from npm with `npm install --save matreshka`.

What version of Matreshka.js is current and how active is it?

The recent releases are v3.0.1 and v3.0.0, both on 2026-10-17, after v2.4.0 from 2017-04-14, and the project describes itself as maintained since 2013. The repository is not archived and the last push was on 2026-10-22, so the 3.0 line is very new.

What optional modules does the matreshka package ship?

The package exports map lists two subpaths beyond the root, ./router and ./parse-form, each with its own type declarations, plus the main entry at ./dist/index.js. The examples directory includes a router example alongside the core ones.

How is Matreshka.js licensed and what is it built with?

The licence field on the repository says MIT, and the README links the MIT License file. The library is written in TypeScript, uses Biome for linting and formatting, Vitest for tests, and requires node >=22.12 to run the tooling.

Official sources

  1. finom/matreshka on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/finom-matreshka.svg)](https://hysenlabs.com/projects/finom-matreshka)

Was this useful?