Store a Small List


Most applications keep a list the user edits later, such as todos, notes, saved records or a task board. The whole list can live in one key-value entry, so a screen loads with a single puter.kv.get() and there is nothing to page through.

You can store the list as an object with an item id key. This lets you add, edit, and delete each item in one call without manually reading the entire list.

Add an Item

To add an item, use the puter.kv.update() method with the id as the path:

const id = crypto.randomUUID();

await puter.kv.update('todos', {
    [id]: { text: 'Buy milk', done: false, at: Date.now() },
});

The id becomes the key you reference later to update or delete that item. Any unique string works, and crypto.randomUUID() is a safe default.

Show the List

To load the list, use the puter.kv.get() method. One read returns every item:

const todos = await puter.kv.get('todos') ?? {};

const items = Object.entries(todos)
    .map(([id, todo]) => ({ id, ...todo }))
    .sort((a, b) => a.at - b.at);

An object has no inherent order, and the stored field order is not preserved on read, so carry an at or order field on each item and sort when you render. At sizes that fit in one entry, sorting in memory costs nothing measurable.

Edit an Item

To change one field of one item, use the puter.kv.update() method with the item's id and the field you are changing:

await puter.kv.update('todos', { [`${ id }.done`]: true });

This updates the specific property of the object with that id, without you having to manually iterate the whole list and update it.

Delete an Item

To remove an item, use the puter.kv.remove() method with its id:

await puter.kv.remove('todos', id);

It also takes several paths in one call, so remove('todos', idA, idB) deletes two items at once.

When to Switch

One entry holds up to 400 KB, which is a few thousand small items. If your list holds more than that, or you expect it to, store a large collection instead, which gives you:

← All recipes