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

mates

v0.0.21

Published

Mates is a front end framework for building web applications

Readme

Mates

Mates is a lightweight framework that focuses on developer experience. It lets you build great web applications easily with it's awesome state management system. It's typescript First Framework. supports typescript all the way.

🚀 Features (MATES)

  • Mutable State: Mutate state directly through setters to update components (views)
  • Actions: Function utilities to help with business logic
  • Templates: Template functions that return lit-html result
  • Events: Event Utilites for event-driven development
  • Setup functions: These are functions to intialise state needed for the view

📦 Installation


npm  install  mates

# or

yarn  add  mates

🧠 Core Concepts

👁 Views: Components of Mates

Views are similar to components in react. They are closure functions with outer function aka Setup function (the S from the MATES), and inner function is called Template function (the T from MATES) which returns html template string.

import { renderView, setter, html } from "mates";

const CounterView = (props) => {
  // setup function: initialise state
  let count = 0;
  const incr = setter(() => count++);

  // template function: returns html template strings.
  return () => html` <div>
    <h1>Count: ${count}</h1>
    <button @click=${incr}>Increment</button>
  </div>`;
};

// Render the view in any html element by passing it's id
renderView(CounterView, "app");

count is a local let variable that holds value. which can only be changed froma setter function. incr is a setter function, that when called, updates the view. it has to be non-async.

Scopes

Mates introduces a new feature called Scopes. an amazing way to share state with child views (components). using Scopes, child components can access parent's state directly without using props.

Formatting Support:

you can install lit-html plugin on your IDE like vs code or cursor for proper formatting for your template strings.

props()

In Mates, props can be passed to child views as object. props is a funciton and not an object but it's passed as object to child component. the view will have to call props() to get the projects object.

const CounterView = () => {
  let count = 0;
  let incr = setter(() => count++);
  return () => html`
    <div>count is: ${count}</div>
    <div>${view(ChildView, { count })}</div>
  `;
};
const ChildView = (props: Props<{ count: number }>) => {
  return html`parent count is : ${props().count}`;
};

note please don't destructure props into local variables in the outer function, as it breaks connection from the parent object. but you can do this in the inner function as inner function gets executed everytime the parent view is updated. so you will have always the latest value.

⚛️ Atoms: Simple Reactive State

Atoms hold mutable values. they can hold any Javascript value like primitive or objects or maps or sets.etc They store a single value that can be read, set (replaced with new value), or updated (if it's an object). They support typescript fully.


import  {  atom  }  from  "mates";

// Create an atom with initial value
const username  =  atom("guest");

// Read the value
console.log(username());  // "guest"
// or
console.log(username.get()); // "guest"

// set the value
username.set("alice");

// you can also Use setter function
username.set((val)  => val.toUpperCase());

// you can also update the value if it's of object type
const address  =  atom({  street:  ""  });

// you are updating just the street value in an object.
address.update((s)  =>  (s.street  =  "newstreet"));

// supports maps or sets:
const nums = atom(new Map());
// modify the map through update.
nums.update(m=>m.set(1, "one"));

const nums = atom(new Set([1,2,3]);
nums.update(s=>s.add(4));
// get the value
nums(); // Set(4) [1,2,3,4]

Counter app using atoms

const CounterView = (props) => {
  // setup function: initialise state
  const count = atom(0);
  const incr = count.set(count() + 1);

  // template function: returns html template strings.
  return () => html` <div>
    <h1>Count: ${count}</h1>
    <button @click=${incr}>Increment</button>
  </div>`;
};

Units: Independent Object-Based State

Units are perfect for managing object-based state with methods and building store utilities. Units have the following pieces

  • Data: any type of javascript value
  • Setters: methods whose name start with (_)
  • Getters: methods that returns data without changing it
  • Actions: async or non-async methods that call getters or setters for getting, setting data.
import { unit } from "mates";

// Create a users unit
const todoList = unit({
  users: [],
  isLoading = false,
  // setter
  _setIsLoading(value) {
    this.isLoading = value;
  },
  // setter
  _setUsers(newUsers) {
    this.users = newUsers;
  },
  // async action that calls setters to set state
  async loadUsers() {
    this._setIsLoading(true);
    this._setUsers(await fetch("/users").then((d) => d.json()));
    this._setIsLoading(false);
  },
  getUsersCount() {
    return this.users.length;
  },
});

📊 Getters: Computed Values

Getters create computed values that only recalculate when their dependencies change.

import { atom, getter } from "mates";

const firstName = atom("John");

const lastName = atom("Doe");

const fullName = getter(() => {
  return `${firstName()}  ${lastName()}`;
});

console.log(fullName()); // "John Doe"

// Only recalculates when dependencies change

firstName.set("Jane");

console.log(fullName()); // "Jane Doe"

🧬 Molecules: group atoms or units or getters into one molecule

import { molecule, atom } from "mates";

class UserStore {
  name = atom("Guest");

  isLoggedIn = atom(false);

  login(username) {
    this.name.set(username);
    this.isLoggedIn.set(true);
  }

  logout() {
    this.name.set("Guest");
    this.isLoggedIn.set(false);
  }
}

// Create a molecule from the class

const userStore = molecule(UserStore);

// Use the molecule

console.log(userStore().name()); // "Guest"

userStore().login("Alice");

console.log(userStore().isLoggedIn()); // true

🔄 XProvider: Context Management

XProvider allows you to provide and consume context across your application.

import { html } from "lit-html";

import { view, useContext } from "mates";

// Create a context class

class ThemeContext {
  theme = "light";

  toggleTheme() {
    this.theme = this.theme === "light" ? "dark" : "light";
  }
}

// Provider component

const ThemeProvider = view(
  (props) => {
    const themeContext = new ThemeContext();

    return () => html`
      <x-provider .value=${themeContext}> ${props().children} </x-provider>
    `;
  },

  { children: [] }
);

// Consumer component

const ThemedButton = view(() => {
  // Get context instance

  const theme = useContext(ThemeContext);

  return () => html`
    <button class="${theme.theme}-theme" @click=${() => theme.toggleTheme()}>
      Toggle Theme (Current: ${theme.theme})
    </button>
  `;
}, {});

🎮 Complete Example

Here's a complete todo list example that showcases Mates' features:

import { html } from "lit-html";

import { view, bubble, atom } from "mates";

// Create state with a bubble

const todos = bubble((setter) => {
  let items = [];

  let newTodoText = "";

  const setNewTodoText = setter((text) => {
    newTodoText = text;
  });

  const addTodo = setter(() => {
    if (newTodoText.trim()) {
      items.push({ text: newTodoText, completed: false });

      newTodoText = "";
    }
  });

  const toggleTodo = setter((index) => {
    items[index].completed = !items[index].completed;
  });

  const deleteTodo = setter((index) => {
    items.splice(index, 1);
  });

  return () => ({
    items,

    newTodoText,

    setNewTodoText,

    addTodo,

    toggleTodo,

    deleteTodo,
  });
});

// Create a view for the todo app

const TodoApp = view(() => {
  return () => {
    const {
      items,

      newTodoText,

      setNewTodoText,

      addTodo,

      toggleTodo,

      deleteTodo,
    } = todos();

    return html`
      <div class="todo-app">
        <h1>Todo List</h1>

        <div class="add-todo">
          <input
            value=${newTodoText}
            @input=${(e) => setNewTodoText(e.target.value)}
            @keypress=${(e) => e.key === "Enter" && addTodo()}
            placeholder="Add new todo"
          />

          <button @click=${addTodo}>Add</button>
        </div>

        <ul class="todo-list">
          ${items.map(
            (item, index) => html`
              <li class=${item.completed ? "completed" : ""}>
                <input
                  type="checkbox"
                  .checked=${item.completed}
                  @change=${() => toggleTodo(index)}
                />

                <span>${item.text}</span>

                <button @click=${() => deleteTodo(index)}>Delete</button>
              </li>
            `
          )}
        </ul>

        <div class="todo-stats">
          <p>${items.filter((item) => !item.completed).length} items left</p>
        </div>
      </div>
    `;
  };
}, {});

// Mount the app

document.body.appendChild(TodoApp);

🔄 Why Mates?

Mates gives you the power and simplicity of React hooks without the React! As a complete framework, it's perfect for:

  • Building lightweight web apps without other heavy frameworks

  • Adding reactivity to existing applications

  • Creating reusable, reactive components

  • Prototyping ideas quickly

📚 Learn More

Check out our examples to see more usage patterns and advanced framework features.

📄 License

MIT