nuxt-protokit
@websideproject/nuxt-protokit

Schema-driven prototyping that turns a TypeScript schema into an interactive, offline-first tool persisted to IndexedDB with Y.js

@websideproject/nuxt-protokit

npm version npm downloads license

Schema-driven prototyping for Nuxt 4. Describe a tool as one TypeScript object — fields, computed values, collections, results and charts — and <ProtoTool> renders it, saved to IndexedDB through Y.js as you type.

  • One schema — 12 field types, derived values, collections with CRUD, result cards, 12 chart and visualization types
  • Offline-first — every edit is a local Y.js update persisted to IndexedDB; tabs stay in sync over BroadcastChannel
  • Headless — use the persistence layer (usePrototype, useProtoMap, useProtoList, useProtoText) without any UI
  • Calendar, item panels, dashboards — ready-made components that read the same schema
  • Migrations, namespaces, encryption — versioned schemas, per-tenant isolation, AES-GCM encrypted storage
  • Corruption recovery and optional server backup to your own endpoints
  • Extensible — custom field and visualization types via defineProtokitExtension
  • Nuxt 4 + Nuxt UI (v3 or v4)

Install

npm install @websideproject/nuxt-protokit yjs y-indexeddb @nuxt/ui tailwindcss
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/ui', '@websideproject/nuxt-protokit'],
  css: ['~/assets/css/main.css'],
  protokit: {
    serverSync: false, // local only; see "Server backup" below
  },
})

The components are styled with Tailwind classes, so Tailwind has to scan the module. Add one @source line to your stylesheet (the path is relative to the CSS file):

/* app/assets/css/main.css */
@import "tailwindcss";
@import "@nuxt/ui";

@source "../../../node_modules/@websideproject/nuxt-protokit";

Quick start

<!-- app/pages/estimator.vue -->
<script setup lang="ts">
import { formatMoney } from '#protokit/utils/formatters'

const estimator = definePrototype({
  key: 'resource-estimator', // also the IndexedDB key
  title: 'Resource Cost Estimator',
  shortTitle: 'Estimator',
  description: 'Estimate a project from team size, salaries and duration.',
  icon: 'i-lucide-calculator',

  fields: {
    teamSize: { type: 'number', label: 'Team size', default: 3, trailing: 'people' },
    avgMonthlySalary: { type: 'number', label: 'Avg. monthly salary', default: 4500, leading: '$' },
    months: { type: 'range', label: 'Duration (months)', min: 1, max: 24, step: 1, default: 6 },
  },

  derived: {
    monthlyCost: { compute: ({ fields }) => fields.teamSize * fields.avgMonthlySalary },
    projectTotal: { compute: ({ fields, derived }) => derived.monthlyCost * fields.months },
  },

  results: [{
    title: 'Cost summary',
    badge: ({ derived }) => derived.projectTotal < 50_000
      ? { label: 'Small project', color: 'primary' }
      : { label: 'Large project', color: 'warning' },
    stats: ({ derived }) => [
      { label: 'Monthly', value: formatMoney(derived.monthlyCost) },
      { label: 'Project total', value: formatMoney(derived.projectTotal) },
    ],
  }],

  visualizations: [{
    type: 'bar-chart',
    title: 'Monthly vs total',
    config: {
      type: 'bar-chart',
      data: ({ derived }) => [
        { label: 'Monthly', value: derived.monthlyCost },
        { label: 'Total', value: derived.projectTotal },
      ],
    },
  }],
})
</script>

<template>
  <ClientOnly>
    <ProtoTool :schema="estimator" />
  </ClientOnly>
</template>

A form, a result card with a badge and two stats, and a bar chart. Change a value and reload: it is still there, read back from IndexedDB. definePrototype, the composables and every Proto* component are auto-imported; helpers with generic names, like formatMoney, are imported from #protokit/utils/… so they never collide with your app's own.

Without the UI:

const { state, derived, collections, isReady } = usePrototype(estimator)
state.teamSize.value = 5 // persisted

In the playground

The repo's playground runs these; the pictures are its CI screenshots.

A tool from one schema — fields, derived values, a result card and a chart:

The resource cost estimator: team size, salary, tooling, duration and overhead inputs; a cost summary with four stats and a Medium Project badge; a monthly cost breakdown bar chart

ProtoCalendar — month, week and day views with drag and drop:

ProtoCalendar week view with five timed events laid out by the hour

Item panels — a detail view for the selected collection item:

A content piece with its URL input, weekly metrics time series and copy templates panels

Visualizations — stat cards, line, area, bar, pie, funnel, benchmark and timeline:

The built-in visualizations on one page

Server backup

With serverSync enabled (the default is true) the client pushes each document's full Y.js state to your backend and asks it for snapshots, which corruption recovery restores from: POST /api/yjs/sync, POST /api/yjs/snapshots/create, GET /api/yjs/snapshots/:key, POST /api/yjs/snapshots/restore, GET /api/yjs/pull (base URL configurable with serverSync: { baseUrl }). Requests that fail are ignored, so the local copy keeps working without a backend; set serverSync: false to make no requests at all. Protokit ships the client side only.

Documentation

Full documentation: websideproject.com/docs/nuxt-protokit

License

MIT