Collections
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"
}
| Field | Required | Limits |
|---|---|---|
name | yes | 64 graphemes |
description | no | 300 graphemes |
items | yes | 1000 items, in display order |
item.note | no | 140 graphemes, 560 bytes |
createdAt | yes | datetime |
Each item's subjectRef is one of:
- A package:
https://npmx.dev/package/<name>, for examplehttps://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 reads straight from your PDS, so it updates right away. The collection page does too for changes made on npmx.dev; changes from other apps can take up to 5 minutes.
- Like counts come from Constellation, usually within seconds.