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

jsongraphs

v1.1.1

Published

Visualise any JSON as a beautiful interactive graph — custom renderer, no dependencies

Downloads

657

Readme

JsonGraphs

High-performance, dependency-free JSON graph visualizer

npm version Bundle Size License TypeScript

Documentation · npm · GitHub

Features

| Feature | Details | |---|---| | 🌳 Tree Layout | Clean left-to-right hierarchy with zero node overlaps | | 🌀 Radial Layout | Organic outward-growing spiral — compact and overlap-free at any scale | | 🔄 Streaming Parser | Progressive loading of large files — the UI never freezes | | 🎨 Theming | Built-in light & dark themes, fully composable custom themes | | 🔍 Search | Full-text Ctrl+K search with live node highlighting | | 🗺️ MiniMap | Viewport-aware minimap with pan indicator | | 🪗 Expand/Collapse | Double-click any node to toggle its subtree | | 🏷️ Captions | __CAPTION__ key injects floating pills and edge labels | | 📦 Zero deps | Pure TypeScript + Canvas 2D — no runtime dependencies |


Installation

npm install jsongraphs
# or
yarn add jsongraphs
# or
bun add jsongraphs

Quick Start

import { JsonGraph, darkTheme } from 'jsongraphs';

const graph = new JsonGraph({
  container: document.getElementById('graph-container')!,
  theme: darkTheme,
  layout: 'tree',   // or 'radial'
  minimap: true,
  toolbar: true,
  search: true,
});

await graph.load({
  project: "My App",
  version: "2.0",
  dependencies: {
    react: "^19",
    typescript: "^5",
  },
});

React / Next.js

"use client";
import { useEffect, useRef } from "react";
import { JsonGraph, darkTheme } from "jsongraphs";

export function GraphViewer({ data }: { data: object }) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!ref.current) return;
    const graph = new JsonGraph({
      container: ref.current,
      theme: darkTheme,
      layout: "tree",
      minimap: true,
      toolbar: true,
      search: true,
    });
    graph.load(data);
    return () => graph.destroy();
  }, [data]);

  return <div ref={ref} style={{ width: "100%", height: "600px" }} />;
}

Next.js: Add "use client" in App Router, or use dynamic(() => import(…), { ssr: false }) in Pages Router.


API Reference

new JsonGraph(options)

| Option | Type | Default | Description | |---|---|---|---| | container | HTMLElement | required | Host element for the canvas | | theme | Theme | defaultTheme | Visual theme | | layout | 'tree' \| 'radial' | 'tree' | Initial layout | | minimap | boolean | true | Show minimap overlay | | toolbar | boolean | true | Show toolbar overlay | | search | boolean | true | Enable Ctrl+K search | | showNodeCount | boolean | true | Show total node count badge | | maxNodes | number | 500_000 | Parser node limit | | maxDepth | number | 200 | Parser depth limit | | onLoad | (nodes, edges) => void | | Called when loading completes | | onError | (err: Error) => void | | Called on parse errors |

Methods

// Data
await graph.load(source)           // File | ReadableStream | Response | string | object

// Layout
graph.setLayout('radial')          // Switch layout

// Viewport
graph.fitView()                    // Fit all visible nodes into view
graph.zoomIn()                     // Zoom in
graph.zoomOut()                    // Zoom out
graph.resetZoom()                  // Reset to 100%

// Theme
graph.setTheme(myTheme)            // Apply a new theme
graph.toggleTheme()                // Toggle light ↔ dark

// Nodes
graph.expandNode(id)               // Expand a collapsed node
graph.collapseNode(id)             // Collapse an expanded node
graph.expandAll()                  // Expand all nodes
graph.collapseAll()                // Collapse all nodes

// Lifecycle
graph.destroy()                    // Unmount and clean up all resources

Data Sources

// JS object
await graph.load({ users: [] });

// JSON string
await graph.load('{"users":[]}');

// Fetch response (streamed progressively)
const res = await fetch('/data.json');
await graph.load(res);

// File input
const file = input.files[0];
await graph.load(file);

// ReadableStream
await graph.load(response.body);

Layouts

Tree Layout

A clean, left-to-right hierarchical layout. Every subtree occupies its own vertical band — zero overlaps regardless of depth.

root
├── users [array]
│   ├── { name: "Alice", role: "admin" }
│   └── { name: "Bob",   role: "user"  }
└── config
    ├── debug: false
    └── version: "2.0"

Radial Layout

An organic, outward-growing spiral layout inspired by natural packing patterns. Nodes radiate from the center with siblings clustered together. Overlap-free at any scale.


__CAPTION__ Keys

Add contextual labels to any node by including a __CAPTION__ key:

{
  "user": {
    "__CAPTION__": "is assigned to",
    "name": "Alice",
    "role": "admin"
  }
}
  • Top-level nodes → floating pill rendered above the node
  • Deeper nodes → label centered on the incoming edge

The __CAPTION__ node is automatically hidden — only its value is used as the label.


Theming

import { defaultTheme, type Theme } from 'jsongraphs';

const myTheme: Theme = {
  ...defaultTheme,
  name: 'custom',
  background: '#0a0a10',
  nodeColors: {
    object:  { fill: '#1a1040', stroke: '#6366f1', text: '#c4b5fd', badge: '#6366f1' },
    array:   { fill: '#0a2010', stroke: '#22c55e', text: '#86efac', badge: '#22c55e' },
    string:  { fill: '#0a1828', stroke: '#3b82f6', text: '#93c5fd', badge: '#3b82f6' },
    number:  { fill: '#1a1400', stroke: '#eab308', text: '#fde047', badge: '#eab308' },
    boolean: { fill: '#1a0a14', stroke: '#ec4899', text: '#f9a8d4', badge: '#ec4899' },
    null:    { fill: '#141414', stroke: '#6b7280', text: '#9ca3af', badge: '#6b7280' },
  },
};

graph.setTheme(myTheme);

Performance

| Metric | Value | |---|---| | Rendering | 60 FPS Canvas 2D | | Max recommended nodes | 500,000 | | Large file loading | Progressive — UI stays responsive | | Bundle size | < 50 kB minzipped | | Dependencies | 0 |


Browser Support

All modern browsers with Canvas 2D and ResizeObserver support:

  • Chrome 88+, Edge 88+, Firefox 90+, Safari 14+

License

MIT © Devian Agency


Contributing

See CONTRIBUTING.md for guidelines.
Security issues: see SECURITY.md.