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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mrt.98/mrtx

v1.0.4

Published

A lightweight GetX-inspired state management library for React

Readme

mrtx

A lightweight, GetX-inspired state management library for React.
Built on a simple controller + observer pattern — no boilerplate, no magic, just plain TypeScript classes.

Install

npm install @mrt.98/mrtx

Peer dependency: React 17 or higher is required.


Core concepts

| Piece | What it does | |---|---| | MrtxController | Base class — extend it, add state as properties, call this.update() to re-render | | MrtxBuilder | React component — creates or finds a controller and re-renders when update() is called | | put / find / deleteInstance / isRegistered | Global DI container — register and retrieve controllers by string key |


Quick start

import { MrtxController, MrtxBuilder } from '@mrt.98/mrtx';

class CounterController extends MrtxController {
  count = 0;

  increment() {
    this.count++;
    this.update(); // triggers re-render
  }
}

export function Counter() {
  return (
    <MrtxBuilder
      id="CounterController"
      init={() => new CounterController()}
      builder={(ctrl) => (
        <div>
          <p>{ctrl.count}</p>
          <button onClick={() => ctrl.increment()}>+1</button>
        </div>
      )}
    />
  );
}

MrtxController

Extend this class for every piece of state in your app.

class MyController extends MrtxController {
  // Add state as plain properties
  loading = false;
  data: string[] = [];

  // Override lifecycle hooks (optional)
  onInit() {
    // called once when the controller is first registered
  }

  onClose() {
    // called when the controller is disposed
  }

  // Call this.update() after any state mutation
  async load() {
    this.loading = true;
    this.update();

    this.data = await fetchData();
    this.loading = false;
    this.update();
  }
}

Methods

| Method | Description | |---|---| | update() | Notifies all listeners to re-render | | onInit() | Lifecycle hook — override to run code on init | | onClose() | Lifecycle hook — override to run cleanup code | | init() | Called internally on first registration | | close() | Called internally on disposal |


MrtxBuilder

React component that connects a controller to the UI.

<MrtxBuilder
  id="MyController"
  init={() => new MyController()}
  autoDispose={true}
  builder={(ctrl) => <div>{ctrl.data}</div>}
/>

Props

| Prop | Type | Required | Default | Description | |---|---|---|---|---| | id | string | No | — | Key used to register/find the controller in the DI container | | init | () => T | No | — | Factory function that creates the controller | | builder | (ctrl: T) => JSX.Element | Yes | — | Render function — called on every update() | | autoDispose | boolean | No | true | If true, disposes the controller when the component unmounts |

You must provide at least one of init or id.

  • Provide both to create and register a controller.
  • Provide only id to find an already-registered controller.

DI Container

put(key, instance, overwrite?, global?)

Register a controller instance.

import { put } from '@mrt.98/mrtx';

put('AuthController', new AuthController(), true, true);
//                                          ^      ^
//                                    overwrite  global (never auto-disposed)

find(key)

Retrieve a registered controller. Throws if the key is not found.

import { find } from '@mrt.98/mrtx';

const auth = find<AuthController>('AuthController');

deleteInstance(key)

Dispose and remove a controller.

import { deleteInstance } from '@mrt.98/mrtx';

deleteInstance('AuthController');

deleteAllInstances()

Dispose and remove all non-global controllers.

import { deleteAllInstances } from '@mrt.98/mrtx';

deleteAllInstances(); // useful on logout / route reset

isRegistered(key)

Check if a controller is currently registered.

import { isRegistered } from '@mrt.98/mrtx';

if (isRegistered('AuthController')) { ... }

updateAllControllers()

Call update() on every registered controller.

import { updateAllControllers } from '@mrt.98/mrtx';

updateAllControllers(); // useful for theme or locale changes

Usage patterns

Pattern 1 — Local controller

Scoped to a single component. Disposed automatically on unmount.

<MrtxBuilder
  id="CounterController"
  init={() => new CounterController()}
  // autoDispose={true} is the default
  builder={(ctrl) => <p>{ctrl.count}</p>}
/>

Pattern 2 — Shared controller

One controller instance, multiple components subscribing to it.

// Component A — creates the controller and keeps it alive
<MrtxBuilder
  id="TodoController"
  init={() => new TodoController()}
  autoDispose={false}
  builder={(ctrl) => <TodoInput ctrl={ctrl} />}
/>

// Component B — finds the same instance by id
<MrtxBuilder
  id="TodoController"
  builder={(ctrl: TodoController) => <TodoList ctrl={ctrl} />}
/>

Pattern 3 — Global controller

Registered once at app startup, accessible everywhere, never disposed.

// main.tsx or App.tsx — register before any component mounts
put('ThemeController', new ThemeController(), true, /* global */ true);

// Any component anywhere in the tree
<MrtxBuilder
  id="ThemeController"
  builder={(ctrl: ThemeController) => (
    <div data-theme={ctrl.theme}>
      <button onClick={() => ctrl.toggle()}>Toggle theme</button>
    </div>
  )}
/>

TypeScript

All APIs are fully typed. The MrtxBuilder component is generic — TypeScript will infer the controller type from init and pass it typed into builder.

class UserController extends MrtxController {
  name = 'Mohammad';
}

<MrtxBuilder
  id="UserController"
  init={() => new UserController()}
  builder={(ctrl) => <p>{ctrl.name}</p>}
  //               ^ ctrl is typed as UserController
/>

License

MIT — Mohammad Al-Thneebat