useProtoCollection

High-level CRUD composable for Y.Array-backed collection lists — reactive items, add/update/remove/move, search, sort, and item count.

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.

Need a Landing Page?

Modern landing pages with optional modules (blog, docs, forms, i18n). Let's discuss your project.

Build Your MVP

Full-stack SaaS development. Expert in database design, multi-tenancy, and scalable architecture.

Deployment Help

Dockerize your backend, set up CI/CD pipelines, deploy to Cloudflare or Hetzner. Early-stage setup.

Suggest a SaaS Tool

Missing a calculator or tool? Suggest what you'd like to see on our site.