Derived & Computed Values
Derived & Computed Values
Derived values turn raw field inputs into meaningful numbers, strings, dates, or structures. They update reactively whenever their dependencies change and are available in results, visualizations, and the produces/consumes graph.
Basic syntax
derived: {
myValue: {
compute: (ctx: ComputeContext) => { /* return any value */ },
}
}
The compute function is synchronous and pure — it should have no side effects.
The ComputeContext
interface ComputeContext {
fields: Record<string, any> // unwrapped current field values
derived: Record<string, any> // earlier derived values (in definition order)
connections: Record<string, any> // data from upstream tools (via consumes)
collections: Record<string, any[]> // CRUD collection items
}
derived.earlierKey, but referencing a later key returns undefined. Define dependencies before dependants.Referencing other derived values
derived: {
// Step 1 — contributionMargin must be defined BEFORE breakEvenUnits
contributionMargin: {
compute: ({ fields }) => fields.pricePerUnit - fields.costPerUnit,
},
// Step 2 — can safely reference derived.contributionMargin
breakEvenUnits: {
compute: ({ fields, derived }) =>
derived.contributionMargin > 0
? Math.ceil(fields.fixedCosts / derived.contributionMargin)
: Infinity,
},
// Step 3 — references two earlier derived values
profitAtForecast: {
compute: ({ fields, derived }) =>
derived.contributionMargin * fields.expectedUnits - fields.fixedCosts,
},
}
Referencing upstream connections
When a tool declares consumes, the resolved values are in ctx.connections:
// schema declares:
consumes: { pricingArpu: 'pricing.arpu' }
// derived can use:
derived: {
ltv: {
compute: ({ fields, connections }) =>
connections.pricingArpu
? connections.pricingArpu / (fields.churnRate / 100)
: null,
},
}
If the upstream tool has not yet produced data (or is not connected), connections.pricingArpu is undefined/null. Always guard against this.
Referencing collections
derived: {
interviewCount: {
compute: ({ collections }) => collections.interviews?.length ?? 0,
},
avgSentiment: {
compute: ({ collections }) => {
const interviews = collections.interviews ?? []
if (!interviews.length) return null
const total = interviews.reduce((sum, i) => sum + (i.sentiment ?? 0), 0)
return total / interviews.length
},
},
}
The deps hint
The deps array is an optional documentation hint listing which fields/derived keys this computation reads. It has no effect on reactivity — useProtoDerived recomputes all derived values whenever any field or collection changes. Use it as a readability aid:
breakEvenUnits: {
deps: ['fixedCosts', 'contributionMargin'],
compute: ({ fields, derived }) => …,
}
Formatting
The format property controls how the value is displayed in results and stat cards. It is purely presentational — the raw computed value is stored and used in subsequent computations.
derived: {
monthlyBurn: {
compute: ({ fields }) => fields.rent + fields.salaries + fields.software,
format: { type: 'money', currency: '€', decimals: 0 },
},
runwayMonths: {
compute: ({ fields, derived }) =>
derived.monthlyBurn > 0
? Math.floor(fields.totalFunds / derived.monthlyBurn)
: Infinity,
format: { type: 'number', decimals: 0, suffix: ' months' },
},
runwayDate: {
compute: ({ derived }) => {
if (!isFinite(derived.runwayMonths)) return null
const d = new Date()
d.setMonth(d.getMonth() + derived.runwayMonths)
return d.toISOString().split('T')[0]
},
format: { type: 'date' },
},
}
Available format types
type | Input | Example output |
|---|---|---|
'money' | number | € 12,345 |
'percent' | number (0–100) | 23.5 % |
'number' | number | 1,234 |
'date' | ISO string | Mar 2026 |
'text' | string | as-is |
Format options: currency, decimals, prefix, suffix.
Using derived values in results
Both results.badge and results.stats receive the full ComputeContext, so derived values are directly accessible:
results: {
badge: ({ derived }) =>
derived.ltvCacRatio >= 3
? { label: 'Healthy LTV:CAC', color: 'success' }
: { label: 'LTV:CAC Too Low', color: 'error' },
stats: ({ derived }) => [
{ label: 'LTV', value: derived.ltv, format: 'money' },
{ label: 'CAC', value: derived.cac, format: 'money' },
{ label: 'LTV:CAC', value: derived.ltvCacRatio, format: 'number' },
],
}
Using derived values in produces
Reference derived values to export them to downstream tools:
produces: {
arpu: 'derived.arpu',
mrrProjection: 'derived.mrrProjection',
}
The string format is 'derived.keyName' — the 'derived.' prefix is required for the resolver to distinguish it from raw field values.
Collections
Collections model CRUD lists of structured items. They map to Y.Arrays for offline-capable persistence with search, sort, modal/inline editing, and preset packs.
Connections
The produces/consumes system lets tools share computed data without tight coupling, forming a reactive data graph across your entire application.