Overview
Formulas allow you to create calculated fields that automatically update based on other field values. The formula system handles dependency resolution automatically, ensuring formulas are evaluated in the correct order even when fields depend on each other. Key Features:- Automatic dependency resolution
- Circular dependency detection
- Support for arithmetic, functions, and array operations
- Real-time recalculation when field values change
- Type-safe comparisons (no automatic type coercion)
Enabling Formulas
Formulas are automatically resolved by the JoyDoc component when enabled in thefeatures prop:
How Formulas Work
Formulas consist of two parts:-
Formula Definition - The calculation expression stored in
doc.formulas -
Formula Application - The link between a field and a formula stored in
field.formulas
- JoyDoc automatically identifies all formulas that reference that field
- Determines the correct evaluation order (handles dependencies automatically)
- Evaluates all dependent formulas
- Updates field values with calculated results
-
Calls
onChangewith the updated document
Formula Structure
Document-Level Formula Definitions
Define formulas at the document level in aformulas array:
Field-Level Formula Applications
Fields that should receive formula results have aformulas property:
Field References
Formulas reference fields by their_id property. You can reference any field in the document:
Basic Arithmetic
Formulas support standard arithmetic operations:"5 + 3"), field references (field1 + field2), or a mix of both (field1 + 5).
Built-in Functions
The formula engine includes 50+ built-in functions across multiple categories.Math Functions
String Functions
Array Functions
Array functions work with table fields and other array values:Logic Functions
Date Functions
Arrow Functions (Lambda Functions)
Formulas support arrow functions for array operations. You can use either Notion-style (->) or JavaScript (=>) syntax:
Working with Table Fields
When working with table fields in lambda functions, access columns directly by their column field ID:Comparison Operators
All comparison operators use strict type checking (no automatic type coercion):Equality (==)
Type and value must be exactly the same:
Inequality (!=)
Returns true if types or values differ:
Greater Than (>)
Only works with numbers:
Less Than (<)
Only works with numbers:
Greater/Less Than or Equal (>=, <=)
Work like > and < but include equality:
Complete Examples
Example 1: Basic Calculation
Example 2: Conditional Logic
Example 3: Table Operations
-
table1[0]- Access first row (zero-indexed) -
table1[0].columnName- Access specific column in first row -
In lambda functions:
(row) => row.columnName- Access column by field ID
Example 4: String Operations
Example 5: Date Operations
Example 6: Complex Table Operations
Dependency Resolution
Formulas automatically resolve dependencies and evaluate in the correct order:multiplySum depends on sum, so it evaluates addAB first, then multiplySum.
Field Types and Formulas
Supported Field Types
Formulas can write calculated values to:-
text- String results -
textarea- String results -
number- Numeric results -
dropdown- String results -
multiSelect- Array results -
date- Timestamp results -
chart- Array of chart line objects
Read-Only Field Types
These field types do NOT support formula writes:-
signature -
image -
file -
table -
collection
sum(map(table1, (row) => row.price))).
Common Patterns
Pattern 1: Calculate Total from Table
Pattern 2: Conditional Calculation
Pattern 3: Filter and Count
Pattern 4: Average with Filter
Pattern 5: String Concatenation
Pattern 6: Multiple Conditions
Integration with Conditional Logic
Formulas and conditional logic work independently:- Formulas - Calculate field values automatically
- Conditional Logic - Control field/page visibility
Error Handling
Formulas are evaluated automatically by JoyDoc. If a formula has errors:- The formula is skipped (doesn’t crash the form)
- Other formulas continue to evaluate
- The field retains its previous value
-
Unknown Function - Function name typo (e.g.,
lenghtinstead oflength) - Undefined Variable - Variable name doesn’t match lambda parameter
- Circular Dependency - Fields reference each other in a loop
- Type Mismatch - Formula result type doesn’t match field type
Best Practices
-
Use Descriptive Formula IDs - Make formula IDs meaningful (
calculateTotalvsf1) -
Add Descriptions - Use the
descfield to document what each formula does -
Handle Null Values - Use
empty()or null checks for optional fields - Test Edge Cases - Test with empty arrays, null values, and zero values
- Keep Expressions Readable - Break complex formulas into multiple steps when possible
-
Use Arrow Functions - Use
=>or->syntax for cleaner array operations - Validate Field Types - Ensure formula results match target field types
- Reference Existing Fields - Ensure referenced fields exist before using them
Troubleshooting
Formula Not Updating
-
Check Formula Feature - Ensure
features.formulas: trueis set -
Check Formula Application - Ensure field has
formulasarray with correctformulareference - Verify Field References - Ensure field IDs in expression match actual field IDs
- Check Field Type - Ensure field type supports formula writes
Formula Errors
- Unknown Function - Check function name spelling
- Undefined Variable - Verify variable names match lambda parameters
- Type Mismatch - Ensure formula result type matches field type
- Circular Dependency - Check for circular references between formulas
Debugging Tips
- Start Simple - Test with basic arithmetic first
-
Check onChange - Verify
onChangeis being called with updated values - Validate Document Structure - Ensure formulas array exists and is properly formatted
- Test Individual Formulas - Isolate formulas to identify issues
Complete Integration Example
-
When
unit_priceorquantitychanges,subtotalis automatically recalculated -
When
subtotalordiscount_percentchanges,discount_amountis recalculated -
When
subtotalordiscount_amountchanges,totalis recalculated - All calculations happen automatically in the correct order