Question on Headless Adaptive Form | Community
Skip to main content
Level 4
February 20, 2026

Question on Headless Adaptive Form

  • February 20, 2026
  • 2 replies
  • 61 views

We are looking to utilize the headless Adaptive form to display the form on an external React SPA. As i understand JSON representation of the form is targeted at /content so it includes the validations, patterns, field limits etc that we configure at component level and that stay under the /content node. 

But in case of complex requirements, where i need to write my own JS functions, this wont work as client libs will never be sent back through the API. Are there any alternatives to do this ? how flexible is the rule editor in handling a little to medium complex validations. Example, an API call that needs to be made on field select, SSN masking, Prevent future date from being picked etc.

2 replies

Adobe Employee
February 23, 2026

Hi ​@kolluax,

Following up on your question about using Headless Adaptive Forms inside your external React SPA, and how to handle more complex validations and logic when AEM clientlibs are not sent to the SPA, here is a consolidated overview.
 

For headless Adaptive Forms you have three main options for “little to medium complex” logic without sending AEM clientlibs to your React SPA:

  1. Use the built‑in rule system / JSON Formula in the form JSON – supports most common validations (required, patterns, min/max, date comparisons, conditional show/hide, etc.), and is executed by the Forms Web SDK in your SPA.
  2. Use AEM custom functions (core‑component forms) – write JS once in an AEM clientlib; it’s exposed into the rule editor and included in the JSON, and then executed client‑side by the Web SDK, including async patterns and API calls.
  3. Add React‑side logic on top of the Web SDK – for UI‑only needs like masking, you can implement React behavior (e.g. SSN mask) and still rely on AEM constraints + Web SDK validation for “real” business rules.

Below is how this maps to your specific questions.

1. How headless form logic runs in your SPA

Headless Adaptive Forms expose a JSON form model that contains:

  • Fields and structure
  • Constraints (required, patterns, min/max, etc.)
  • Rules and expressions (JSON Formula)

On the frontend, this JSON is executed by the AEM Forms Web SDK, which:

  • Maintains form state
  • Evaluates rules and event handlers
  • Provides hooks to bind that state to your React components (@aemforms/af-core@aemforms/af-react-renderer@aemforms/af-react-components)

Because all rule logic is represented in JSON + metadata, it works the same way when the form is rendered headlessly in your SPA, without shipping AEM clientlibs or DOM‑dependent JS.

References

2. Using the Rule Editor for “little to medium complex” logic

For Adaptive Forms based on Core Components (which are the basis for headless), the modern Rule Editor lets you define most business rules without writing raw JavaScript:

  • When/Then/Else rules with nested conditions
  • Show/hide, enable/disable, set/reset values
  • Validate fields/panels/forms
  • Invoke Form Data Model (FDM) services (external APIs) and handle success/failure
  • Use temporary variables, query parameters, browser context, custom events, repeatable panels, etc.

These rules are compiled into JSON Formula and executed by the Forms Web SDK in your SPA.

Reference

3. Custom functions – the recommended alternative to raw clientlibs

For use cases that go beyond standard Rule Editor capabilities, the recommended approach is custom functions, not arbitrary JS in clientlibs:

  • You implement reusable JavaScript functions in an AEM client library.
  • Functions are documented with JSDoc annotations, so AEM can auto‑discover them.
  • They become selectable in the Rule Editor (e.g. as “Function Output” or via the async rule type).
  • At runtime, they are executed by the Forms Web SDK and work in both “headful” and headless contexts.

Typical use cases:

  • Complex validations across multiple fields
  • Custom calculations
  • Integration with external APIs (sync or async)
  • Conditional behavior not expressible as a single JSON Formula expression

References

4. Asynchronous logic and external API calls

For scenarios like “call an API on field change and use the response in rules”, you have two supported patterns:

  1. Invoke Service via Form Data Model (no JS)

    • Configure an FDM service pointing to your REST API.
    • In the Rule Editor:
      • When a field is changed → Invoke Service.
      • Map inputs from fields and map service outputs back to target fields.
      • Configure success and failure handlers (what happens on a 200 vs error).
  2. Async custom functions (Promise‑based)

    • Implement a custom function that returns a Promise (e.g., validateSsnWithApi(ssn): Promise<boolean>).
    • In the Rule Editor, use Async Function Call to invoke it, and configure success/failure callbacks via rules.

Both options are fully supported in Adaptive Forms (Core Components) and work with Headless Adaptive Forms in your React SPA.

References

5. Mapping to your specific examples

a) API call when a field is selected/changed

  • Option 1: FDM + Invoke Service rule (no custom JS).
  • Option 2: Async custom function invoked via Async Function Call rule, using the API response to set values, show/hide panels, enable/disable submit, etc.

b) SSN masking

We recommend separating display masking from validation:

  • Masking/UI: Implement the mask in the React component (e.g., using a masking library).
  • Validation: Enforce the SSN format and rules via:
    • Pattern constraints in the field properties, and/or
    • A custom validation function exposed in the Rule Editor.

That way, UI behavior stays in your SPA; business validation logic remains centrally defined in AEM and is still executed by the Forms runtime in headless mode.

c) Prevent future date selection

This is fully achievable without custom JS:

  • Configure a max date constraint (e.g., today) in the date field properties where supported, or

  • Add a validation rule:

    • When the date field is changed
    • Then validate the field value using a date comparison expression (e.g., “value is before or equal to today()”).

For more complex date logic (e.g., must be at least 18 years in the past), a small custom function can be introduced and reused across forms.

6. Recommended implementation pattern for your React SPA

Putting it all together, our recommended pattern is:

  1. Model & rules in AEM

    • Build the form with Adaptive Forms Core Components.
    • Put as much behavior as possible in Rule Editor (When/Then/Else, constraints, Invoke Service).
    • Add custom functions only where needed (complex validation, bespoke API logic).
  2. Render via Web SDK in React

    • Use the official libraries (@aemforms/af-core@aemforms/af-react-renderer@aemforms/af-react-components) or the starter kit to fetch and render the headless form JSON in your SPA.
  3. SPA‑specific UX in React

    • Keep purely presentational/UX logic (masking, special focus behavior, animations) inside React components.
    • Let AEM handle rules, validations, and API integrations so that logic is shared across channels.

Thanks
Pranay

kolluaxAuthor
Level 4
February 23, 2026

@Pranay_M  - Thank you for a detailed response. Option 2, “Custom functions – the recommended alternative to raw clientlibs” caught my attention. Does this apply to On Prem ? I see on the articles it applies to Cloud.

kolluaxAuthor
Level 4
February 23, 2026

Here is what i did, so far, please correct me if i am wrong with my understanding.

  1. I created a Custom client library, added JS into it which has a JSDoc based annotation. Here is the sample

/**
 * Checks whether the input date is at least 18 years ago.
 * @param {string} dateString Date string in YYYY-MM-DD format.
 * @returns {boolean}
 */
function isAtLeast18YearsOld(dateString) {
  var inputDate = new Date(dateString);
  var today = new Date();
  var date18YearsAgo = new Date();
  date18YearsAgo.setFullYear(today.getFullYear() - 18);
  return inputDate <= date18YearsAgo;
}

 

  1. I have included the client library category into my Adaptive form.
  2. On the React SPA side, when they hit the form URL .model.json
  3. These components whose validation is set to function output , do not fire and i see function not found on browser console.
  4. When i inspect JSON of the form, i see below not sure if i should add my JS into this directory ? if it is a cloud based config.
fd:customFunctionsUrl":"/adobe/forms/af/customfunctions/L2NvbnRlbnQvZm9ybXMvYWYvY29yZS1mb3JtLWV4YW1
  1. I modified this with /etc.clientlibs of my project and then the JS started working. But since API call is returning that #5 location i suppose i cannot change it. 

Is this even correct, the approach ?

 

Thanks,

Abhishek

VishalKa5
Level 6
March 2, 2026

Hi ​@kolluax ,

 

  • Headless Adaptive Forms expose only the form structure, validations, and rules as JSON via the /content endpoint.

  • AEM client libraries (custom JS/CSS) are not delivered to the external React SPA.

  • Custom JavaScript written in AEM will not work in a headless implementation.

  • The Rule Editor supports required fields, pattern validations, min/max limits, conditional logic, calculations, and basic date restrictions.

  • Complex features like API calls, SSN masking, async validation, and third-party integrations must be handled in the React application.

  • Best practice: Use AEM for form definition and basic rules, and use React for advanced logic and integrations.

Thanks,
Vishal