npmx.dev
Integrations

Collections

Make package collections from any atproto app

Collections live in your account

A collection is an ordered list of npm packages with notes, stored as a dev.npmx.collection record in your own atproto repository. npmx.dev is one way to make and view them, not the only one. Write a record with that shape from any app, script or PDS, and it shows up on your npmx.dev profile.

Collections are public, like everything else in an atproto repository.


The record

The schema is in lexicons/dev/npmx/collection.json. Record keys are TIDs.

{
  "$type": "dev.npmx.collection",
  "name": "Arg parsers",
  "description": "CLI argument parsers worth knowing.",
  "items": [
    { "subjectRef": "https://npmx.dev/package/commander", "note": "The default choice" },
    { "subjectRef": "https://npmx.dev/package/citty" },
    { "subjectRef": "at://did:plc:example/dev.npmx.collection/3m5k5baifas2b" }
  ],
  "createdAt": "2026-11-01T00:00:00.000Z"
}
FieldRequiredLimits
nameyes64 graphemes
descriptionno300 graphemes
itemsyes1000 items, in display order
item.noteno140 graphemes, 560 bytes
createdAtyesdatetime

Each item's subjectRef is one of:

  • A package: https://npmx.dev/package/<name>, for example https://npmx.dev/package/@vitejs/plugin-vue. This is the same identifier npmx likes use. It names the package; it doesn't depend on npmx.dev being up.
  • Another collection: its AT-URI, with the author's DID (not their handle), for example at://did:plc:example/dev.npmx.collection/3m5k5baifas2b. The included collection stays live, so whoever owns it keeps curating it.

npmx.dev shows anything else as an unrecognized item, so the author can see and fix it, rather than hiding the collection.

Showing nested collections

npmx.dev shows an included collection as a link with its name, author and size, rather than merging its items into the parent list, so readers can see whose list each package came from. If your viewer expands included collections inline, stop at 2 levels deep and when a collection includes one you're already showing.

Liking a collection

Likes reuse dev.npmx.feed.like, with the collection's AT-URI as subjectRef. Don't use the subject strong reference for collections: it pins a specific version, and collections change.

{
  "$type": "dev.npmx.feed.like",
  "subjectRef": "at://did:plc:example/dev.npmx.collection/3m5k5baifas2a",
  "createdAt": "2026-11-01T00:00:00.000Z"
}

Following a collection

A follow is a dev.npmx.graph.follow record whose subject is the collection's AT-URI. A like says a collection is good; a follow says you want updates about it. Your own collections count as followed without a record. To follow a package, add it to a collection you follow.

{
  "$type": "dev.npmx.graph.follow",
  "subject": "at://did:plc:example/dev.npmx.collection/3m5k5baifas2a",
  "createdAt": "2026-11-01T00:00:00.000Z"
}

Make one with curl

Use an app password, not your main password. Replace PDS with your PDS (for most Bluesky accounts, https://bsky.social).

PDS=https://bsky.social
HANDLE=you.example.com

# 1. Get a session
SESSION=$(curl -s "$PDS/xrpc/com.atproto.server.createSession" \
  -H 'Content-Type: application/json' \
  -d "{\"identifier\":\"$HANDLE\",\"password\":\"$APP_PASSWORD\"}")
TOKEN=$(echo "$SESSION" | jq -r .accessJwt)
DID=$(echo "$SESSION" | jq -r .did)

# 2. Create the collection
curl -s "$PDS/xrpc/com.atproto.repo.createRecord" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d @- <<EOF
{
  "repo": "$DID",
  "collection": "dev.npmx.collection",
  "record": {
    "\$type": "dev.npmx.collection",
    "name": "Arg parsers",
    "items": [
      { "subjectRef": "https://npmx.dev/package/commander", "note": "The default choice" },
      { "subjectRef": "https://npmx.dev/package/citty" }
    ],
    "createdAt": "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"
  }
}
EOF

The response contains the new record's uri, ending in its record key. Your collection is at https://npmx.dev/collections/<your handle>/<record key>.

To edit a collection, use com.atproto.repo.putRecord with the same record key, and pass the cid you last read as swapRecord so you don't overwrite someone else's edit made in the meantime.


When it shows up

  • Your profile and the collection page read straight from your PDS, so they update right away.
  • Like counts come from Constellation, usually within seconds.
Copyright © 2026