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

locsync

v1.0.0

Published

Ultra-light localStorage synchronizer that only writes changed values. ESM/CJS/UMD, zero dependencies.

Readme

Locsync

Ultra-light localStorage synchronizer – declaratively push a set of key/value pairs and Locsync writes only what actually changed.

Locsync gives you a single function to idempotently ensure a collection of values is present and current in localStorage while skipping redundant writes. Great for persisting lightweight UI / session state, feature flags, cached timestamps, and user preferences.

Features

  • Write Avoidance – Skips values that haven't changed (reduces churn & flash of stale state)
  • Declarative – Call once per render / event loop; it's idempotent
  • Deterministic – No timers, no async race conditions
  • TypeScript First – Strongly typed schema
  • Zero Dependencies – Tiny, auditable
  • UMD / ESM / CJS Builds – Works everywhere you need it

When To Use

Use Locsync when you want a simple, low-level primitive to mirror ephemeral runtime data into localStorage without building a reactive layer. If you need diffs, events, or reactive replay, pair it with your state management solution.

Installation

Using npm

npm install locsync

Using pnpm

pnpm add locsync

Using yarn

yarn add locsync

Quick Start

Basic Example

import locsync from "locsync";

// Define your schema
const userSettings = {
	theme: "dark",
	language: "en",
	notifications: "enabled"
};

// Sync to localStorage
locsync(userSettings);

In Browser (UMD)

<!DOCTYPE html>
<html>
	<head>
		<title>My App</title>
	</head>
	<body>
		<script src="https://unpkg.com/locsync/dist/index.umd.js"></script>
		<script>
			// locsync is available globally
			locsync({
				userId: "12345",
				preferences: "compact"
			});
		</script>
	</body>
</html>

TypeScript Usage

import locsync from "locsync";
import type { Schema } from "locsync";

const schema: Schema = {
	apiKey: "your-api-key",
	cacheExpiry: "3600000"
};

locsync(schema);

API

locsync(schema: Schema)

Synchronizes the provided schema with localStorage.

  • Parameters:
    • schema (Schema): An object where keys are localStorage keys and values are the data to store
  • Returns: void

Schema Type

type Schema = Record<string, string>;

All values must be strings. If you need to store complex data, stringify it first:

locsync({
	userData: JSON.stringify({ name: "John", age: 30 }),
	settings: JSON.stringify({ theme: "dark" })
});

How It Works (Internals)

  1. For each key in the schema, locsync checks if the value has changed since the last sync
  2. If changed, it updates localStorage with the new value
  3. It maintains a "previous value" cache to detect changes efficiently
  4. Unchanged values are skipped to optimize performance

Examples

User Preferences

const preferences = {
	"app-theme": "light",
	"sidebar-collapsed": "false",
	"language": "en-US"
};

locsync(preferences);

Form Data Persistence

const formData = {
	"form-step": "2",
	"user-email": "[email protected]",
	"draft-content": JSON.stringify(editorContent)
};

locsync(formData);

Application State

const appState = {
	"current-view": "dashboard",
	"last-login": new Date().toISOString(),
	"session-id": generateSessionId()
};

locsync(appState);

Advanced Usage

Serializing Complex Data

You can provide only string values. Serialize objects/arrays ahead of time:

locsync({
	filters: JSON.stringify(activeFilters),
	cache: JSON.stringify({ v: 2, expires: Date.now() + 60000 })
});

Namespacing Keys

Keep keys scoped to your app / feature to avoid collisions:

const ns = (k: string) => `myapp:${k}`;
locsync({
	[ns("session")]: sessionId,
	[ns("draft:v1")]: draftContentHash
});

Guarding for Non-Browser Environments

If you share code with SSR or Node contexts, wrap the call:

if (typeof window !== "undefined" && "localStorage" in window) {
	locsync(schema);
}

Batching With Other Persistence

Call Locsync after you finalize derived values (e.g., in a React effect):

useEffect(() => {
	locsync({ "ui:theme": theme, "ui:collapsed": String(isCollapsed) });
}, [theme, isCollapsed]);

Avoiding Unbounded Growth

Locsync never deletes keys. If you version keys (e.g., feature:x:v2) remember to prune old ones with a manual cleanup script if needed.

Performance Notes

  • Each call does: Object.keys + up to 2 localStorage ops per changed key
  • Unchanged keys: 1 getItem + 0 writes
  • Changed keys: 1 getItem + 2 writes (value + previous marker)
  • Complexity: O(n) where n = number of keys provided

Tips:

  • Group related keys and call Locsync once per UI cycle
  • Avoid passing large serialized payloads repeatedly; hash them first if size matters

Troubleshooting

| Symptom | Likely Cause | Fix | | ----------------------------- | ----------------------------- | ----------------------------------------------- | | Values not updating | Passing same string each call | This is expected – only changed values flush | | Quota exceeded | Storing very large JSON blobs | Compress or prune; use IndexedDB for large data | | Nothing happens SSR | No localStorage on server | Guard calls (see Non-Browser example) | | Key collision with other libs | Generic key names | Prefix / namespace keys |

FAQ

Why store a previous copy (prev-locsync-<key>)?
To detect changes without reading and parsing your original serialized structures. It lets us skip rewriting unchanged values.

Can I store numbers / booleans?
Yes—convert them to strings first (JS does this implicitly in template literals): locsync({ count: String(count) });

Does it debounce / throttle?
No. Keep calls minimal yourself—idempotence makes extra calls inexpensive.

Will it work in private mode?
Yes in most browsers, unless storage is disabled. Wrap in a try/catch for hardened scenarios.

How do I remove keys?
Manually call localStorage.removeItem(key); Locsync only syncs provided keys.

Roadmap

  • Optional diff / changed keys return value
  • Pluggable storage adapter (sessionStorage, memory)
  • Key expiration helper utilities
  • Devtool / logging flag

Star the repo to follow progress or open an issue to upvote a feature.

Browser Support

  • Chrome 60+
  • Firefox 55+
  • Safari 12+
  • Edge 79+

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Security

Locsync writes only the literal strings you provide. Do not store secrets, tokens, or PII in localStorage—it is accessible to any script running on the page.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

See CHANGELOG.md for version history.