Portal Community

When to Use Transforms

Scenarioread transformwrite transform
Date stored as ISO 8601, displayed as locale stringISO → locale dateLocale date → ISO
Amount stored in cents, displayed in pounds÷ 100× 100
Boolean stored as 0/1 integer, displayed as Yes/No0/1 → false/truefalse/true → 0/1
CSV string stored, displayed as array of tagssplit(',')join(',')

Registering a Transform

import { registerBindingTransform } from '@atlas-forms/form-engine-js';

// Register once at application startup
registerBindingTransform('isoToLocaleDate', (value: string): string => {
  if (!value) return '';
  const date = new Date(value);
  return date.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
  // e.g. '15 Jan 2026'
});

registerBindingTransform('localeDateToIso', (value: string): string => {
  if (!value) return '';
  // Parse '15 Jan 2026' → '2026-01-15'
  const date = new Date(value);
  return date.toISOString().split('T')[0];
});

registerBindingTransform('centsToGbp', (value: number): number => {
  return typeof value === 'number' ? value / 100 : 0;
});

registerBindingTransform('gbpToCents', (value: number): number => {
  return typeof value === 'number' ? Math.round(value * 100) : 0;
});

Using Transforms in a Schema

// Date field: stored as ISO, displayed as locale date
{
  "id":   "contract-start",
  "type": "date-picker",
  "label": "Contract Start Date",
  "binding": {
    "source": "$json",
    "path":   "contract.startDate",
    "transform": {
      "read":  "isoToLocaleDate",
      "write": "localeDateToIso"
    }
  }
}

// Amount field: stored in cents, displayed in pounds
{
  "id":   "invoice-amount",
  "type": "number",
  "label": "Invoice Amount (£)",
  "binding": {
    "source": "$json",
    "path":   "invoice.amountPence",
    "transform": {
      "read":  "centsToGbp",
      "write": "gbpToCents"
    }
  }
}

One-Directional Transforms

You can apply only a read transform (display formatting without write-back conversion) or only a write transform:

// Read-only display transform: show a code as a human-readable label
{
  "binding": {
    "source": "$json",
    "path":   "statusCode",
    "transform": {
      "read": "statusCodeToLabel"   // write omitted — raw code is saved on submit
    }
  }
}

Transform Function Signature

type BindingTransformFn = (value: any) => any;

// Transforms must be pure functions — no side effects
// They may receive undefined/null and should handle gracefully
registerBindingTransform('myTransform', (value) => {
  if (value == null) return '';   // Safe null handling
  return String(value).toUpperCase();
});
Register Transforms Early Transforms are looked up by name at mount time. If a schema references a transform name that has not been registered, the engine logs a warning and skips the transform (the raw value is used). Always register all transforms in your application bootstrap before any forms are rendered.