usePrototype
usePrototype
The high-level facade. Combines useProtoDoc, useProtoMap, useProtoList, useProtoDerived, and useProtoOutputs into one composable call. Start here when building a custom tool UI.
Signature
function usePrototype(
schema: PrototypeSchema,
docKey?: string, // defaults to schema.key
): ProtoToolState
Return value
interface ProtoToolState {
// Y.js document
doc: Y.Doc
isReady: ComputedRef<boolean> // true once IndexedDB has loaded
// Form fields — one Ref per field key
state: Record<string, Ref<any>>
// CRUD collections — one ProtoCollectionState per collection key
collections: Record<string, ProtoCollectionState>
// Computed derived values
derived: ComputedRef<Record<string, any>>
// Compute context (for passing to results/viz functions)
computeContext: ComputedRef<ComputeContext>
// Actions
reset(): void // reset all fields to schema defaults
}
Basic usage
<script setup lang="ts">
const { state, derived, computeContext, isReady, reset } = usePrototype(mySchema)
</script>
<template>
<div v-if="!isReady">Loading…</div>
<div v-else>
<UInput v-model="state.mrr.value" type="number" label="MRR" />
<p>ARR: {{ derived.arr }}</p>
<UButton @click="reset">Reset</UButton>
</div>
</template>
Field refs
Each field in the schema becomes a Ref<any> inside state:
const { state } = usePrototype(schema)
// Read
console.log(state.mrr.value)
// Write (Y.js transaction, persisted to IndexedDB)
state.mrr.value = 15000
Changes written to state.x.value are immediately persisted to the Y.js document's IndexedDB store and (when online) queued for server sync.
Collections
const { collections } = usePrototype(schema)
// 'interviews' maps to schema.collections.interviews
const interviews = collections.interviews
interviews.items.value // Ref<Item[]> — reactive list
interviews.add({ name: 'Alice', notes: '…' })
interviews.update(itemId, { notes: 'updated' })
interviews.remove(itemId)
interviews.move(itemId, 1) // move down by 1 position
interviews.search.value = 'pain'
interviews.filtered.value // Ref<Item[]> — search-filtered
interviews.count.value // Ref<number>
Derived values
const { derived } = usePrototype(schema)
// derived is a ComputedRef — access .value
console.log(derived.value.ltvCacRatio) // automatically recomputed when fields change
ComputeContext
Pass computeContext.value to your own result / visualization rendering functions:
const { computeContext } = usePrototype(schema)
// Use in template
const badge = computed(() => schema.results?.badge?.(computeContext.value))
const stats = computed(() => schema.results?.stats?.(computeContext.value) ?? [])
Custom doc key
// Two independent instances of the same schema — separate storage
const toolA = usePrototype(breakEvenSchema, 'break-even-scenario-a')
const toolB = usePrototype(breakEvenSchema, 'break-even-scenario-b')
Readiness guard
isReady becomes true once IndexeddbPersistence has finished loading stored data. Always guard renders behind v-if="isReady" to avoid flash-of-default-values:
<template>
<ClientOnly>
<div v-if="!isReady" class="animate-pulse">Loading saved data…</div>
<ProtoForm v-else :schema="schema" :state="state" />
</ClientOnly>
</template>
reset()
Resets all field refs to their schema default values and writes the defaults to the Y.js document. Collection items are not cleared by reset() — clear them individually if needed.
const { reset } = usePrototype(schema)
// Prompts user and resets on confirm
async function handleReset() {
if (confirm('Reset all values?')) {
reset()
}
}
Scoping to a namespace (shared Y.Doc)
When multiple tools share a single Y.Doc, pass the existing doc's scoped Y.Map directly instead of letting usePrototype create its own document:
// In a tool wrapper component — shared doc comes from a workspace store
const workspaceStore = useWorkspaceStore(workspaceId)
const toolMap = workspaceStore.getToolMap(toolKey) // returns doc.getMap(`fields:${toolKey}`)
// Read/write within this scoped Y.Map
const { state, derived } = useProtoMap(toolMap, schema.fields)
Use useProtoMap, useProtoList, and useProtoCollection directly in this case — usePrototype always creates its own Y.Doc and is not suitable for shared-document architectures.
Overview
All composables auto-imported by protokit — from the high-level usePrototype facade to the low-level Y.js primitives.
useProtoDoc
Low-level Y.js document lifecycle composable — creates, caches, and manages Y.Doc instances with IndexedDB persistence, optional server sync, and corruption detection.