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

overwatch-ts

v1.1.8

Published

A blazing-fast, zero-boilerplate state management library for React & Next.js

Readme

Overwatch — A Lightweight Sensible State Manager

Alt text Build Size Version

🚀 What’s New?

Overwatch now supports Server-Side Rendering SSR (v 1.0.7)


A minimal API with developer flexibility, TypeScript-first state-management solution, inspired by the simplicity of Zustand, It's a super lightweight yet expressive state management library for React Js & Next Js, built on the singleton design pattern. It offers support for global and instance-specific middlewares, immutability, batched updates, and custom event communication — all designed to be used without extensive boilerplate.

Goal with OverWatch was to prioritize reusability, a positive developer experience, and clear component-level state and event tracking.

Read the full documentation at overwatchts.in/docs.


🚀 What Is OverWatch?

OverWatch evolved from an internal utility that leveraged the singleton and pub-sub patterns. We've now refined it for React, incorporating hooks, strong typing, and automatic cleanup to make it accessible to everyone.

OverWatch is:

  • Built with React hooks and TypeScript (offering 100% type safety for predictable development).
  • Lightweight — you won't find context providers, reducers, or dispatches here.
  • Ideal for managing both component-level and application-level shared state, providing flexibility for various needs.
  • Inspired by Zustand: builds upon the simplicity and performance-first design of Zustand but reimagines it with a focus on advanced middleware, event-driven architecture, immutable state enforcement, and without the need for central store boilerplate.

Why This Exists: State management should be straightforward & Sensible

If you've ever felt that Redux was too comprehensive for your needs, or that libraries like Zustand didn't offer enough specific control, OverWatch aims to provide a balanced approach. It combines a minimal API with flexibility to enhance your developer experience.


Architecture: How It Works

At its core, OverWatch uses a pub-sub pattern to manage state effectively:

  • Every state key functions as a communication channel.
  • Components can subscribe to updates on these channels, acting as listeners.
  • You can publish changes to state, either globally or specifically.
  • Middleware pipelines run before changes are published, allowing for custom logic.
  • Batching ensures that multiple updates are grouped efficiently, optimizing performance.
  • Immutability is maintained, helping your state remain consistent and easy to reason about.

How to Use: Getting Started

Installation

To get started with OverWatch-TS, simply install it using npm or yarn:

# Using npm
npm install overwatch-ts

# Using yarn
yarn add overwatch-ts

Step 1: createSharedState(key, initialValue)

This function initializes a shared state value globally. While optional, it's helpful for establishing a default state before any component mounts.

If createSharedState isn't used, useSharedState will automatically create the key the first time it's accessed, providing a flexible starting point.

// Setting an initial theme
createSharedState('theme', { mode: 'dark' });

Step 2: useSharedState(key)

Use this hook in any component to read and update your shared state. It's your primary interface for interacting with OverWatch.

import { useSharedState } from 'overwatch-ts'; 

const ThemeSwitcher = () => {
  const [theme, setTheme] = useSharedState<{ mode: string }>('theme');

  const toggleTheme = () => {
    setTheme({ mode: theme.mode === 'dark' ? 'light' : 'dark' });
  };

  return (
    <button onClick={toggleTheme}>
      Switch to {theme.mode === 'dark' ? 'Light' : 'Dark'} Mode
    </button>
  );
};

Step 3: usePicker(key, selectorFn)

This hook allows you to extract only a specific part of a shared state object, which can help optimize component rendering by preventing unnecessary re-renders.

import { usePicker } from 'overwatch-ts'; 

const ThemeIndicator = () => {
  const mode = usePicker('theme', t => t.mode);

  return <div>{mode === 'dark' ? '🌑' : '☀️'}</div>;
};

Why and when to use?

  • It helps prevent unnecessary re-renders, improving application performance.
  • It's particularly useful for larger state objects where you only need to react to changes in one specific field.

Step 4: applyMiddleware(key, middlewareFn)

Use this to attach one or more global middlewares to a shared state key. These middlewares will apply to all updates for that specific state key.

import { applyMiddleware, createSharedState } from 'overwatch-ts'; 

// A simple logger for all 'theme' state changes
const globalThemeLogger = (newValue, next) => {
  console.log('Global Theme Change:', newValue);
  next(newValue); // Remember to call 'next' to continue the update
};

createSharedState('theme', { mode: 'dark' });
applyMiddleware('theme', globalThemeLogger);

(NOTE: If both instance-specific and global middlewares are applied to the same state, instance-specific middlewares will run first.)

Step 5: Instance-specific Middleware (Component Level)

If you need middleware to apply only within a specific component, you can pass it directly when using useSharedState.

import { useSharedState } from 'overwatch-ts'; 

const MyComponentWithLocalLogging = () => {
  const localLogger = (val, next) => {
    console.log('This runs only in this specific component:', val);
    next(val);
  };

  const [theme, setTheme] = useSharedState('theme', {
    middleware: [localLogger],
  });

  // ...rest of your component
};

Component Communication: Beyond State Management

Step 6: useBroadcast and useEvent

These hooks are designed for one-time communications, such as triggering a logout action, showing a modal, or sending a specific notification across your application. While distinct from state management, they integrate seamlessly for a comprehensive communication solution.

Broadcast an event:

import { useBroadcast } from 'overwatch-ts'; 

const LogoutButton = () => {
  const broadcast = useBroadcast();

  const handleLogout = () => {
    // Perform logout logic...
    broadcast('LOGOUT'); // Emit the 'LOGOUT' event
  };

  return <button onClick={handleLogout}>Logout</button>;
};

Listen for an event:

import { useEvent } from 'overwatch-ts'; 
import { useNavigate } from 'react-router-dom'; // Example for React Router

const AuthListener = () => {
  const navigate = useNavigate();

  useEvent('LOGOUT', () => {
    console.log('Logout event received. Redirecting...');
    navigate('/login');
  });

  return null; // This component primarily serves as a listener
};

What is Component Communication?

In many applications, especially those with deep component trees or when React Context feels too rigid, there's a need for cross-component communication that's both decoupled and clear. OverWatch's event system addresses this by:

  • Offering a central event bus built on the pub-sub model.
  • Leveraging custom React hooks for subscribing to and publishing events.
  • Automatically unsubscribing events when components unmount, preventing memory leaks.
  • Tracking which component subscribed to which event for better debugging.
  • Being fully written in TypeScript and easily tree-shakable for efficient bundling.

Ideal Use Cases: Where OverWatch Excels

  • Decoupled communication between unrelated components.
  • Managing temporary global states (e.g., flash messages, user actions).
  • Providing an alternative to prop drilling or over-reliance on React Context.

✨Server-Side Rendering (SSR) Support

Overwatch supports SSR (Server Side Rendering) via a dedicated ServerStore interface, giving you full control over how shared state is created, serialized, and hydrated across the client-server boundary.

How to use (✨Easy!)

  • On the server, create a new store instance using createServerStore().
  • Set and read state via that store during rendering.
  • On the client, hydrate the store using Hyrated wrapper before your app renders.

That's it 3 steps


🧩 The ServerStore Interface

Use createServerStore() to get a per-request store instance:

const serverStore = createServerStore();

Use serverStore.getSnapshot() to pass it to the client, and serverStore.hydrate() or useHydratedStore on the client to Hydrate client-side stores with no flickering.


🧪 Example 1 – SSR in React (Node Server)

// Server: Express/Node
import { createServerStore } from 'overwatch-ts';

const serverStore = createServerStore();
serverStore.set('theme', 'dark'); // Inject initial state

const app = renderToString(
  <App store={serverStore} />
);

// Send snapshot to client
const initialState = serverStore.getSnapshot();
res.send(`
  <script>window.__OVERWATCH_SNAPSHOT__ = ${JSON.stringify(initialState)}</script>
`);
// Client: Hydrate snapshot
import { useHydratedStore } from 'overwatch-ts';

if (typeof window !== 'undefined' && window.__OVERWATCH_SNAPSHOT__) {
  useHydratedStore(window.__OVERWATCH_SNAPSHOT__);
}

⚡ Example 2 – With Next.js (App Router)

'use client'

import { createServerStore } from 'overwatch-ts';

export const serverStore = createServerStore();
import { Hydrated } from 'overwatch-ts';
export async function getServerSideProps() {
  // Set some state server-side

  serverStore.set('theme', { mode: 'dark' });

  return {
    props: {
      initialSnapshot: serverStore.getSnapshot(),
    },
  };
}

export default function Home({ initialSnapshot }) {
  return (
    // Wrapper that renders children only after client is fully hydrated
     <Hydrated snapshot={initialSnapshot}>
      <ToogleTheme /> // client *components
    </Hydrated>
  );
}

On the client

"use client"
export const ToogleTheme = () => {
  const [theme, setTheme] = useSharedState('theme');
  return (<>
    <h1>{theme.mode}</h1>
  </>)
}

✅ Summary

  • Use createServerStore() to create scoped state per request.
  • Populate initial state using createSharedState(...).
  • Use store.getSnapshot() and pass it to the client.
  • Hydrate on the client using Hydrated wrapper before using hooks.
  • All hooks internally default to the global store unless a store is passed manually. (Not Recommended, only for advance use cases)

📜 License

MIT — feel free to fork and adapt OverWatch for your projects.


Built with purpose, and reusability in mind.