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

@collidecreatives/edit-mode

v0.4.0

Published

Tiny browser edit mode overlay for collecting client copy changes.

Readme

@collidecreatives/edit-mode

Tiny browser edit mode overlay for collecting client copy changes.

It lets a client open a page with ?edit=true, click visible text, make copy tweaks, then copy a structured JSON change list.

Install

npm install @collidecreatives/edit-mode

Quick start

import { initClientEditMode } from "@collidecreatives/edit-mode";

initClientEditMode({
  brandName: "Client Edit Mode",
  sessionKey: "mm-edit-mode",
});

Open any page with:

https://example.com/about?edit=true

Static browser script

Use the IIFE build if a site cannot bundle npm packages (e.g. plain WordPress themes):

<script
  src="/edit-mode.js"
  data-brand-name="Client Edit Mode"
  data-session-key="client-edit-mode"
></script>

The browser build auto-initialises and reads data-* attributes on its own <script> tag (data-brand-name, data-query-param, data-query-value, data-session-key, data-accent-colour, data-editable-selector, data-skip-selector). It only activates when ?edit=true is present, when the session key is already set, or when you initialise manually with enabled: true.

Framework guides

Every guide below follows the same shape: enqueue/import the package, then gate activation with enabled — computed server-side — for password protection.

WordPress

Enqueue the static IIFE build from your theme's functions.php:

function collide_edit_mode_enqueue() {
    wp_enqueue_script(
        'collide-edit-mode',
        get_template_directory_uri() . '/assets/edit-mode.js',
        [],
        '1.0.0',
        true
    );

    $password_ok = isset($_GET['edit']) && hash_equals(EDIT_MODE_PASSWORD, $_GET['edit']);

    wp_add_inline_script(
        'collide-edit-mode',
        'window.CollideEditMode.initClientEditMode({ enabled: ' . ($password_ok ? 'true' : 'false') . ', brandName: "Site Edit Mode" });',
        'after'
    );
}
add_action('wp_enqueue_scripts', 'collide_edit_mode_enqueue');

Define EDIT_MODE_PASSWORD in wp-config.php (outside the web root's public reach, never in a theme file committed to a public repo):

define('EDIT_MODE_PASSWORD', getenv('EDIT_MODE_PASSWORD'));

Statamic

Import the npm package in your site's Vite entry and check the password in an Antlers/PHP context, exposing only a boolean to the front end:

{{-- resources/views/layout.antlers.html --}}
<script>
  window.editModeEnabled = {{ if segment_1 == 'edit' && get:edit == env('EDIT_MODE_PASSWORD') }}true{{ else }}false{{ /if }};
</script>
// resources/js/site.js
import { initClientEditMode } from "@collidecreatives/edit-mode";

initClientEditMode({
  enabled: window.editModeEnabled,
  brandName: "Client Edit Mode",
});

EDIT_MODE_PASSWORD lives in .env, never in a versioned Antlers template or the JS bundle.

Next.js

Check the password in a Server Component (App Router) and pass the result to a small Client Component boundary — initClientEditMode touches window/document, so it must run in a "use client" file:

// app/edit-mode.tsx
"use client";
import { useEffect } from "react";
import { initClientEditMode } from "@collidecreatives/edit-mode";

export function EditMode({ enabled }: { enabled: boolean }) {
  useEffect(() => {
    const instance = initClientEditMode({ enabled, brandName: "Site Edit Mode" });
    return () => instance.destroy();
  }, [enabled]);

  return null;
}
// app/layout.tsx
import { cookies } from "next/headers";
import { EditMode } from "./edit-mode";

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const password = (await cookies()).get("edit")?.value;
  const enabled = password === process.env.EDIT_MODE_PASSWORD;

  return (
    <html>
      <body>
        {children}
        <EditMode enabled={enabled} />
      </body>
    </html>
  );
}

EDIT_MODE_PASSWORD stays server-only in .env (not NEXT_PUBLIC_*), so it never reaches the client bundle.

Astro

Check the password in frontmatter (runs server-side) and pass the boolean into a script:

---
// src/layouts/Layout.astro
const editModeEnabled = Astro.url.searchParams.get("edit") === import.meta.env.EDIT_MODE_PASSWORD;
---

<script define:vars={{ editModeEnabled }}>
  import("@collidecreatives/edit-mode").then(({ initClientEditMode }) => {
    initClientEditMode({ enabled: editModeEnabled, brandName: "Site Edit Mode" });
  });
</script>

EDIT_MODE_PASSWORD (no PUBLIC_ prefix) is only readable server-side by Astro, so it never ships in client JS.

Options

initClientEditMode({
  enabled: undefined,
  queryParam: "edit",
  queryValue: "true",
  sessionKey: "collide-edit-mode",
  storageKey: "collide-edit-mode:draft",
  autoSave: true,
  brandName: "Edit Mode",
  accentColour: "#1e40af",
  editableSelector: "h1,h2,h3,h4,h5,h6,p,li,blockquote,figcaption,label,legend,dt,dd,th,td,a,button",
  skipSelector: "[data-no-edit],[data-edit-mode-skip],form,input,textarea,select,option,script,style,svg,canvas,iframe",
  onCopy: (payload) => console.log(payload),
});

Password-protect edit mode

queryValue (e.g. ?edit=happydays instead of ?edit=true) only raises the bar slightly — whatever value you set there ships inside the public JS bundle, so it's not a real secret.

To keep the password server-only, check it in server-rendered code (where the real value can live in an env var, never a client-visible one) and pass the result straight into enabled. The client bundle then never contains the password at all. See the framework guides above for the exact pattern in WordPress, Statamic, Next.js, and Astro — the shared shape is: compare the query string (or a cookie) server-side, then hand the client a plain boolean.

Navigation while editing

Internal links are rewritten to keep ?edit=true when clients move around the site. Link text is editable. To visit a link while edit mode is on:

  • select the link and use Open link in the panel, or
  • Ctrl/-click the link.

Drafts auto-save before navigation, on each edit, and when the page is hidden.

Opt out in templates

<nav data-no-edit>
  ...
</nav>

Payload

{
  "site": "example.com",
  "page": "/about",
  "pageTitle": "About",
  "url": "https://example.com/about?edit=true",
  "timestamp": "2026-07-01T00:00:00.000Z",
  "changes": [
    {
      "path": "#hero > h1: \"Old heading\"",
      "tag": "H1",
      "original": "Old heading",
      "new": "New heading"
    }
  ]
}

Build

npm run build

Outputs:

  • dist/index.js
  • dist/index.cjs
  • dist/index.d.ts
  • dist/browser.global.js

Publish

npm publish --access public

If publishing privately, configure npm/GitHub Packages first.