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

@morph-sdk/core

v0.1.0

Published

Framework-agnostic frecency engine for adaptive UIs

Downloads

27

Readme

@morph-ui/core

A tiny, framework-agnostic library that makes UIs adapt to individual user habits. Track interactions, rank by frecency, surface what matters — zero settings pages needed.

4.5KB · Zero dependencies · Works anywhere JS runs

Install

npm install @morph-ui/core
# or
pnpm add @morph-ui/core

Quick start

import { morph } from '@morph-ui/core'

// Record user interactions
morph.track('sidebar', 'dashboard')
morph.track('sidebar', 'tasks')
morph.track('sidebar', 'tasks')

// Get items ranked by frecency (most used + most recent first)
morph.rank('sidebar')
// → [{ id: 'tasks', score: 2.0, rank: 1, isActive: true },
//    { id: 'dashboard', score: 1.0, rank: 2, isActive: true }]

// Check if a specific item is still actively used
morph.isActive('sidebar', 'dashboard') // → true

How it works

Morph uses a frecency algorithm — a combination of frequency and recency with exponential decay. Items used often and recently score high. Items ignored gradually fade.

score = oldScore × e^(-daysSinceLastUse × decayConstant) + 1
  • Use something daily → score keeps growing
  • Stop using it → score fades naturally over days/weeks
  • One accidental click → barely moves the needle

All data stays in localStorage by default. No server, no tracking, no privacy concerns.

API

morph.track(group, id)

Record an interaction. Call this when a user clicks, opens, or engages with an item.

morph.track('sidebar', 'settings')

morph.rank(group)

Get all tracked items in a group, sorted by frecency score (highest first). Applies read-time decay — scores reflect current relevance, not stored values.

const items = morph.rank('sidebar')
// [{ id: 'tasks', score: 8.7, rank: 1, isActive: true },
//  { id: 'dashboard', score: 3.2, rank: 2, isActive: true },
//  { id: 'settings', score: 0.4, rank: 3, isActive: false }]

Returns MorphRankedItem[]:

| Field | Type | Description | |-------|------|-------------| | id | string | The item identifier | | score | number | Current decayed frecency score | | rank | number | Position in group (1 = most used) | | isActive | boolean | Whether score is above the active threshold |

morph.isActive(group, id)

Check if a specific item is still "active" (decayed score above threshold). Returns false for items that were never tracked.

morph.isActive('sidebar', 'dashboard') // → true or false

morph.reset(group)

Clear all tracked data for a group.

morph.reset('sidebar')

morph.configure(config)

Swap storage backend or tune decay behavior.

morph.configure({
  storage: myCustomStorage,       // swap storage backend
  defaults: {
    decayConstant: 0.05,          // how fast scores fade (higher = faster)
    activeThreshold: 1.0,         // minimum score to be "active"
  },
  groups: {
    commands: { decayConstant: 0.1 },  // per-group overrides
  },
})

Use cases

Sidebars — frequently used sections float to the top, unused ones collapse

const items = morph.rank('sidebar-nav')
const sorted = navItems.sort((a, b) => {
  const rankA = items.find(i => i.id === a.id)?.rank ?? Infinity
  const rankB = items.find(i => i.id === b.id)?.rank ?? Infinity
  return rankA - rankB
})

Command palettes — most-used commands surface first

Dashboards — widgets you check daily stay prominent, ignored ones de-emphasize

Navigation menus — adapts to what each user actually uses

Settings panels — frequent settings at the top, rest collapses into "Advanced"

CLI tools — surface most-used subcommands first in help output

Custom storage

The storage interface is intentionally simple and sync. Implement MorphStorage to use any backend:

import type { MorphStorage } from '@morph-ui/core'

const myStorage: MorphStorage = {
  get(key) { /* return string | null */ },
  set(key, value) { /* store string */ },
  delete(key) { /* remove key */ },
  keys(prefix) { /* return keys matching prefix */ },
}

morph.configure({ storage: myStorage })

For async backends (Cloudflare KV, databases, etc.), use an in-memory cache that hydrates on init and flushes writes in the background.

Multiple instances

The default export is a singleton. For multiple independent instances:

import { Morph } from '@morph-ui/core'

const appMorph = new Morph()
const editorMorph = new Morph()

What Morph stores

Each tracked item persists three fields:

{
  "lastUsed": 1746523200000,
  "useCount": 14,
  "score": 8.73
}

Keys in localStorage follow the format morph:<group>:<id>.

Morph vs analytics (PostHog, Mixpanel, etc.)

| | Analytics tools | Morph | |---|---|---| | Who benefits | Product team | End user | | Data goes to | External server | localStorage (never leaves device) | | Latency | Weeks (analyze → redesign → ship) | Instant | | Granularity | Aggregate (all users) | Individual (this user) | | Privacy | Requires consent | No GDPR concerns |

They're complementary — analytics tells you what to build, Morph makes what you built adapt to each person.

License

MIT