Core Concepts

The mental model behind protokit — schemas, Y.js documents, the produces/consumes data graph, and how everything connects.

Core Concepts

Five ideas underpin everything in protokit. Understanding them makes the rest of the docs straightforward.

1. The schema is the single source of truth

A PrototypeSchema object controls every aspect of a prototype:

PrototypeSchema
├── fields          → form inputs rendered by ProtoForm
├── sections        → grouped form inputs (tabs or collapsible panels)
├── collections     → CRUD lists (tasks, records, entries…)
├── derived         → computed values recomputed on every change
├── results         → badge + stat cards shown below the form
├── visualizations  → charts, tables, timelines
├── cards           → summary cards for dashboard views
├── actions         → copy, export-markdown, reset with confirmation
├── produces        → keys this prototype writes to the shared output map
├── consumes        → upstream prototype.field paths this prototype reads
└── layout          → custom dashboard layout (tabs, rows, columns)

You define the schema once. Every component — form, results, visualizations, CRUD modals — reads from it at runtime. Changing a field label is one line.

2. Every prototype lives in a Y.js document

Every prototype is backed by a Y.js document (Y.Doc). Y.js is a CRDT library that provides:

  • Shared data types: Y.Map (key-value), Y.Array (ordered list), Y.Text (plain text)
  • Local-first storage: changes go to IndexedDB immediately, before any network round-trip
  • Automatic merge: concurrent edits in multiple tabs or devices merge without conflicts

When you write <ProtoTool :schema="mySchema" doc-key="my-tool" />, the module:

  1. Opens (or reuses) a Y.Doc identified by doc-key
  2. Attaches IndexeddbPersistence — the doc survives page reloads
  3. Starts syncing to the server when connectivity is available (via the yjs-sync module)
           Y.Doc "my-tool"
          ┌──────────────────────────────┐
          │  Y.Map  "fields"             │  ← form state
          │  Y.Map  "outputs:my-tool"   │  ← produces values
          │  Y.Array "collection:items" │  ← CRUD items
          │  Y.Map  "draft:item-abc"    │  ← modal draft
          └──────────────────────────────┘
                   │              ▲
           IndexedDB          server sync
           (always)           (when online)

3. Derived values are pure functions

Derived values are synchronous, side-effect-free functions that transform the current state into a new value:

derived: {
  monthlyCost: {
    compute: ({ fields }) => fields.seats * fields.pricePerSeat,
    format:  { type: 'money', currency: '' },
  },
  annualCost: {
    compute: ({ fields, derived }) => derived.monthlyCost * 12,
    format:  { type: 'money', currency: '' },
  },
}

The compute function receives a ComputeContext:

PropertyTypeContains
fieldsRecord<string, any>Unwrapped current field values
derivedRecord<string, any>Previously computed derived values (definition order)
connectionsRecord<string, any>Data flowing in from upstream tools
collectionsRecord<string, any[]>CRUD collection items

Derived values are computed in definition order. A derived value may reference earlier ones via derived.earlier, but referencing a later key returns undefined.

4. Produces / Consumes — the data graph

Prototypes can share computed data without tight coupling via produces and consumes:

// Prototype A — writes to a shared output map
produces: {
  unitCost:  'derived.unitCost',
  margin:    'derived.contributionMargin',
}

// Prototype B — reads from Prototype A's output map
consumes: {
  upstreamUnitCost: 'prototype-a.unitCost',
}

Internally:

  • produces writes values into a Y.Map named outputs:{schemaKey}
  • consumes reads from the source tool's outputs: map, exposing values in ctx.connections

When both prototypes share the same Y.Doc (the recommended pattern), data flows instantly and offline. When they use separate documents, useProtoConnections observes the source document reactively.

5. Collections are Y.Arrays with schema-driven CRUD

A collection is a persistent, offline-capable list of structured items:

collections: {
  tasks: {
    label: 'Tasks',
    fields: {
      title:    { type: 'text',     label: 'Title',       required: true },
      priority: { type: 'segmented', label: 'Priority',
                  options: [
                    { value: 'low',    label: 'Low' },
                    { value: 'medium', label: 'Medium' },
                    { value: 'high',   label: 'High' },
                  ] },
      done:     { type: 'toggle',   label: 'Done',        default: false },
    },
    searchable: true,
    editMode:   'modal',
  }
}

The useProtoCollection composable wraps the underlying Y.Array and provides reactive items, add, update, remove, move, search, filtered, sorted, and count — all working offline automatically.

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.