useProtoCollection
useProtoCollection
High-level CRUD for Y.Array-backed lists. Wraps useProtoList with search, sort, filtered views, and count.
Signature
function useProtoCollection(
doc: Y.Doc,
key: string, // Y.Array name inside the doc
schema: CollectionSchema,
): ProtoCollectionState
Return value
interface ProtoCollectionState {
// Reactive state
items: Ref<CollectionItem[]> // all items, unfiltered
filtered: ComputedRef<CollectionItem[]> // search-filtered items
sorted: ComputedRef<CollectionItem[]> // search-filtered + sorted items
count: ComputedRef<number> // total item count
// Search
search: Ref<string> // two-way binding for search input
// Sort
sortKey: Ref<string | null>
sortDir: Ref<'asc' | 'desc'>
// CRUD operations
add(item: Partial<CollectionItem>): string // returns new item id
update(id: string, patch: Partial<CollectionItem>): void
remove(id: string): void
move(id: string, delta: number): void // +1 = move down, -1 = move up
// Draft helpers (for modal edit mode)
getDraft(id: string): Record<string, any> | null
saveDraft(id: string, data: Record<string, any>): void
clearDraft(id: string): void
}
Basic usage
const { doc } = useProtoDoc('my-tool')
const interviews = useProtoCollection(doc, 'interviews', schema.collections.interviews)
// Add an item — auto-generates a UUID `id`
const newId = interviews.add({
name: 'Alice',
sentiment: 'positive',
notes: 'Loves the product',
})
// Update
interviews.update(newId, { notes: 'Loves the product, will pay €49/mo' })
// Remove
interviews.remove(newId)
// Reorder
interviews.move(newId, -1) // move up one position
// Search
interviews.search.value = 'alice'
console.log(interviews.filtered.value) // only items matching 'alice'
// Count
console.log(interviews.count.value) // 1
In templates
<script setup lang="ts">
const { doc, isReady } = useProtoDoc('my-tool')
const competitors = useProtoCollection(doc, 'competitors', schema.collections.competitors)
</script>
<template>
<div v-if="isReady">
<!-- Search -->
<UInput v-model="competitors.search.value" placeholder="Search competitors…" />
<!-- List -->
<div v-for="item in competitors.filtered.value" :key="item.id">
<span>{{ item.name }}</span>
<UButton @click="competitors.remove(item.id)" icon="i-lucide-trash" />
</div>
<!-- Add -->
<UButton @click="competitors.add({ name: 'New Co' })">Add Competitor</UButton>
<!-- Count badge -->
<UBadge>{{ competitors.count.value }}</UBadge>
</div>
</template>
Item structure
Every item stored in the collection has an auto-generated id field:
interface CollectionItem {
id: string // UUID, assigned on add()
[fieldKey: string]: any // schema field values
}
Search behavior
When schema.searchable: true and search.value is non-empty, filtered returns items where any string field contains the search string (case-insensitive). All fields are searched, not just specific ones.
Sort behavior
When schema.sortable: true:
// Sort by threat level ascending
competitors.sortKey.value = 'threat'
competitors.sortDir.value = 'asc'
// sorted.value now returns items sorted by threat
sorted applies after filtered — search + sort work together.
Max items
If schema.maxItems is set, add() silently does nothing when the limit is reached. Check count.value >= schema.maxItems to show a "limit reached" message in UI.
Low-level: useProtoList
useProtoCollection wraps useProtoList, which provides the raw Y.Array operations:
function useProtoList(
doc: Y.Doc,
key: string,
options?: { maxItems?: number },
): ProtoListState
useProtoList returns { items, add, update, remove, move } without search, sort, or schema awareness. Use it when you need direct Y.Array access.
useProtoDoc
Low-level Y.js document lifecycle composable — creates, caches, and manages Y.Doc instances with IndexedDB persistence, optional server sync, and corruption detection.
Overview
All globally registered components — ProtoTool, ProtoCrudModal, ProtoForm, ProtoDebugPanel, field components, and visualization components.