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 🙏

© 2025 – Pkg Stats / Ryan Hefner

modelez

v0.1.2

Published

A lightweight reactive state management library for Vanilla JavaScript and React, offering dynamic reactivity, async resource handling, and seamless integration with modern web apps.

Readme

Modelez

Modelez is a lightweight and powerful reactive state management library for Vanilla JavaScript and React. It provides an elegant and efficient way to manage application state, logic, and asynchronous resources. Modelez supports dynamic reactivity, conditional reactive states, multiple storages, and seamless integration with React.


Features

  • 🔄 Reactive State: Define and subscribe to reactive states with ease.
  • Async Resource Management: Handle async logic with built-in support for promises and Suspense.
  • 🔃 Conditional Reactivity: Fine-tuned control over reactive updates.
  • 🛠️ Multiple Storages: Manage state across multiple storage backends.
  • 🧩 VanillaJS and React Support: Use Modelez with plain JavaScript or integrate seamlessly into React.

Installation

Install Modelez via npm:

npm install modelez

via yarn:

yarn add modelez

Basic Usage

Vanilla JavaScript

Modelez provides reactive state management for Vanilla JavaScript applications.

import { model, container, effect } from "modelez";

// Set up the HTML structure
document.body.innerHTML = `<button id="clickMe"></button>`;

// Create a container to manage all models
const app = container();

// Define a Counter model
const CounterModel = model("counter", ({ state }) => {
  const count = state(0);

  return {
    count,
    increment() {
      count((prev) => prev + 1);
    },
  };
});

const counter = app.get(CounterModel);
const button = document.getElementById("clickMe");

// Create an effect to reactively update the button text
effect({ count: counter.count }, (deps) => {
  button.textContent = `Count: ${deps.count}`;
});

// increase counter when button clicked
button.addEventListener("click", () => {
  counter.increment();
});

React Integration

Modelez integrates seamlessly with React through hooks and providers.

import { useReactive, Provider } from "modelez/react";
import { app } from "./app";
import { CounterModel } from "./counterModel";

// Counter component
export const Counter = () => {
  const { count, increment } = useReactive(CounterModel);

  return <button onClick={increment}>Count: {count}</button>;
};

export const App = () => {
  return (
    <Provider container={app}>
      <Counter />
    </Provider>
  );
};

Advanced Usage

Using Multiple Storages

import { container, model, createInMemoryStorage } from "modelez";

const getUrlParams = () => new URLSearchParams(window.location.search);

// create URL state storage
const urlStateStorage = {
  get(key) {
    const urlParams = getUrlParams();
    return urlParams.get(key);
  },
  set(key, value) {
    const urlParams = getUrlParams();
    urlParams.set(key, value);
    // Update the URL without reloading the page
    const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
    window.history.pushState({}, "", newUrl);
  },
  has(key) {
    return getUrlParams().has(key);
  },
};

// Define storages
const inMemoryStorage = createInMemoryStorage();
const storages = {
  default: inMemoryStorage, // Default storage
  url: urlStateStorage, // URL-based storage
};

// Create a container with multiple storages
const app = container({ storages });

// Define a model with states stored in different storages
const AppModel = model("app", ({ state }) => {
  // State stored in default (in-memory) storage
  const localState = state("localCount", 0);

  // State stored in URL storage
  const urlState = state("url:page", "home");

  return {
    localState,
    urlState,
    incrementLocal() {
      localState((prev) => prev + 1);
    },
    setPage(page) {
      urlState(page);
    },
  };
});

// Access and use the model
const appModel = app.get(AppModel);

// Update and log the states
appModel.incrementLocal();
console.log("Local State:", appModel.localState());

appModel.setPage("about");
console.log("URL State (Page):", appModel.urlState());

Async Resource Management

Modelez makes it simple to handle asynchronous data fetching.

Defining a Resource

import { model } from "modelez";

const BlogListModel = model("blogList", ({ resource }) => {
  const posts = resource(() => fetch("/api/posts").then((res) => res.json()));

  return {
    posts,
    async reload() {
      await posts.reload();
    },
  };
});

Using the Resource in React

import { useReactive } from "modelez/react";

const BlogListWithLoadingIndicator = () => {
  const { posts } = useReactive(BlogListModel);

  if (posts.loading) {
    return <LoadingIndicator />;
  }

  if (posts.error) {
    return <Error error={posts.error} />;
  }

  return (
    <ul>
      {posts.value.map((post) => (
        <BlogPost key={post.id} post={post} />
      ))}
    </ul>
  );
};

Promise-Like Resources Outside React

The resource object implements the PromiseLike interface, allowing you to use .then() for direct access.

const blogList = app.get(BlogListModel);

blogList.posts.then((posts) => {
  console.log(posts);
});

Using Suspense for Async Resources

import { Suspense } from "react";
import { useReactive } from "modelez/react";

const BlogList = () => {
  const { posts } = useReactive(BlogListModel);

  return (
    <ul>
      {posts.value.map((post) => (
        <BlogPost key={post.id} post={post} />
      ))}
    </ul>
  );
};

const BlogListWithSuspense = () => {
  return (
    <ErrorBoundary>
      <Suspense fallback={<LoadingIndicator />}>
        <BlogList />
      </Suspense>
    </ErrorBoundary>
  );
};

Dependency Injection with Container

import { container } from "modelez";
import { useExternalHook } from "./externalHook";
import { ExternalComponent } from "./externalComponent";

// Create a named container for dependency injection
export const dependencies = container().named({
  ExternalComponent,
  useExternalHook,
});

export const MyComp = () => {
  const result = dependencies.useExternalHook(); // Use the injected hook

  if (result) {
    return <dependencies.ExternalComponent />; // Render the injected component
  }

  return null;
};

Unit Testing Example

import { dependencies, MyComp } from "./myComp";
import { render } from "@testing-library/react";

describe("MyComp", () => {
  let restore: VoidFunction;

  beforeEach(() => {
    // Backup the current dependencies before mocking
    restore = dependencies.$container.backup();
  });

  afterEach(() => {
    // Restore the original dependencies after each test
    restore?.();
  });

  it("should render ExternalComponent if result = true", () => {
    // Mocking useExternalHook to return true
    dependencies.useExternalHook = () => true;

    // Mocking ExternalComponent to render a simple div
    dependencies.ExternalComponent = () => <div>ok</div>;

    // Render the component
    const { getByText } = render(<MyComp />);

    // Verify that "ok" text is displayed
    getByText("ok");
  });

  it("should not render ExternalComponent if result = false", () => {
    // Mocking useExternalHook to return false
    dependencies.useExternalHook = () => false;

    // Render the component
    const { queryByText } = render(<MyComp />);

    // Verify that "ok" text is not displayed
    expect(queryByText("ok")).toBeNull();
  });
});

API Reference

model(name: string, factory: Function)

Defines a model with reactive states and logic.

  • name: A unique identifier for the model.
  • factory: A function returning the model logic and reactive states.

container()

Creates a container to manage and resolve models.

useReactive(model: Model)

A React hook to use a model's reactive states and methods.

effect(dependencies: object, callback: Function)

Tracks dependencies and re-runs the callback when they change.

resource(initializer: Function)

Creates a reactive async resource.