usePrototype

The high-level facade composable that combines document management, field sync, CRUD, derived computation, and cross-tool outputs into a single call.

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.

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.