SapotaCorp

Forms in SFRA: Validation, Errors, and Persistence Patterns

Every B2C Commerce SFRA site has dozens of forms: login, register, checkout, account update, support. The form framework is opinionated and produces inconsistent results when developers fight it. The patterns that work are not in the documentation.

Forms in SFRA: Validation, Errors, and Persistence Patterns

Key takeaways

  • SFRA forms are 3 layers: XML form definition (fields, types, mandatory flags, length limits, regex validators), controller action (reads submitted values, runs validation, calls business logic), and template render (standard HTML form). Developers who fight the framework end up with inconsistent error messages and broken accessibility.
  • Validation runs server-side in the form binding layer, not in custom controller logic. The framework reports field-level errors via the form object; templates render them inline. Custom server-side validation that bypasses the binding layer breaks the standard error-rendering pattern.
  • Persistence on error is the pattern most teams miss. When validation fails, the form must re-render with the submitted values pre-filled so the customer does not retype 10 fields. SFRA's form binding handles this automatically if used correctly; custom flows lose it without careful design.
  • CSRF protection is built in but must be invoked. Every form needs the appropriate token retrieval and validation in the controller action. Skipping it leaves forms vulnerable to cross-site request forgery; the security audit will flag every omission.

Every B2C Commerce SFRA site has dozens of forms. Login. Register. Checkout shipping address. Checkout billing address. Account update. Password reset. Contact us. Support request. The SFRA forms framework is opinionated about how all of these should work. Developers who fight the framework end up with inconsistent error messages, broken accessibility, and validation that fires in the wrong order.

Sapota's Salesforce team has shipped forms across retail and B2B SFRA sites. The patterns that work are not always in the documentation but become reflexive after a few sprints.

The SFRA form framework

SFRA forms are defined in XML files (cartridge/forms/default/...), processed server-side by the form binding layer, and rendered client-side via standard HTML form elements. Three things make up a form's behavior:

1. Form definition (XML). Declares the fields, their types, whether they are mandatory, length limits, regex validators.

2. Form action (controller code). Reads submitted values, runs validation, calls business logic, returns either success or rendered form with errors.

3. Form rendering (ISML). Reads the form object from pdict and renders the fields, validation errors, and submit button.

The default flow is server-side first: form submission posts to a controller endpoint, server validates and re-renders the page with errors if needed. Client-side validation is a progressive enhancement; the server is the source of truth.

Form definition patterns

A standard form definition (forms/default/billing.xml):

<?xml version="1.0"?>
<form xmlns="http://www.demandware.com/xml/form/2008-04-19">
  <field formid="firstName" type="string" mandatory="true"
         label="label.firstname" min-length="2" max-length="50"
         missing-error="error.firstname.missing"/>
  <field formid="email" type="string" mandatory="true"
         label="label.email" regexp="^[^@]+@[^@]+\.[^@]+$"
         missing-error="error.email.missing"
         parse-error="error.email.invalid"/>
</form>

Two attributes matter most:

  • missing-error: the error message key when the field is empty.
  • parse-error: the error message key when the field fails type/regex validation.

These reference resource keys (resolvable to localized strings). All error messages are in resource files, not hardcoded in XML.

Server-side validation that holds up

The controller reads the submitted form, runs clearFormElement if rejecting, and re-renders. A typical pattern:

server.post('Handle', function (req, res, next) {
  var billingForm = server.forms.getForm('billing');

  // Validation hook for custom rules beyond what XML covers.
  if (!isPostalCodeValid(billingForm.postal.value, billingForm.country.value)) {
    billingForm.postal.valid = false;
    billingForm.postal.error = Resource.msg(
      'error.postal.invalid', 'forms', null
    );
  }

  if (!billingForm.valid) {
    res.render('checkout/billing/billingDetails', {
      billingForm: billingForm
    });
  } else {
    // Persist and continue.
    saveBillingAddress(billingForm);
    res.redirect(URLUtils.url('Checkout-Submit'));
  }

  next();
});

Two patterns to internalize:

XML for structural validation, code for business rules. XML handles missing, length, regex. Code handles cross-field validation (passwords must match), business validation (postal code valid for country), database checks (email already in use).

Re-render the form on error, redirect on success. The post-redirect-get pattern prevents form re-submission on browser back. Use res.render when the form has errors; use res.redirect when it succeeds.

Error rendering in ISML

The form rendering pattern that produces consistent UX:

<form action="${URLUtils.url('Billing-Handle')}" method="POST">
  <isinclude template="components/forms/csrf"/>

  <div class="form-group ${pdict.billingForm.firstName.error ? 'has-error' : ''}">
    <label for="firstName"><isprint value="${Resource.msg('label.firstname', 'forms', null)}"/></label>
    <input type="text" id="firstName" name="${pdict.billingForm.firstName.htmlName}"
           value="${pdict.billingForm.firstName.htmlValue}"
           class="form-control"
           required="${pdict.billingForm.firstName.mandatory}">
    <isif condition="${pdict.billingForm.firstName.error}">
      <div class="invalid-feedback"><isprint value="${pdict.billingForm.firstName.error}"/></div>
    </isif>
  </div>

  <button type="submit"><isprint value="${Resource.msg('action.submit', 'forms', null)}"/></button>
</form>

Three things to enforce in every form template:

1. CSRF token included. Use the standard components/forms/csrf include. Forms without CSRF are vulnerable to cross-site request forgery.

2. htmlName and htmlValue from the form object. The framework generates the right name attribute and pre-fills the value on re-render. Hand-rolling these breaks the binding.

3. Error display inline with the field. Errors next to the field they reference, not aggregated at the top. Better UX and better accessibility.

Client-side validation

Client-side validation provides immediate feedback. SFRA ships with a jQuery-based client validator that reads the same form definition the server uses. The pattern:

  • Native HTML5 validation runs first (the required attribute, basic type checks).
  • jQuery handles complex validation (regex, custom rules) on blur or before submit.
  • On submit, the form posts to the server regardless; the server is the source of truth.

The mistake to avoid: client-side-only validation. A determined user can bypass it via curl or developer tools. Every validation rule must exist server-side; client-side is enhancement.

Sapota's form discipline

Five practices that ship reliable forms:

1. One controller per form action. A Login-Handle for login, Register-Handle for registration. Avoid multi-purpose controllers that branch on form type. Easier to test, easier to maintain.

2. All error messages in resource files. No hardcoded strings in code or templates. Localization happens by adding locale-specific resource files.

3. Server-side validation duplicated in code for cross-field rules. XML handles per-field. Cross-field (passwords match, billing country requires postal) handled in the controller after server.forms.getForm().

4. CSRF on every state-changing form. Login, register, checkout, account update, anywhere data is written. CSRF is not optional.

5. Accessibility from the start. Label-for-input pairing, aria-invalid on error fields, focus management after submit error (focus the first invalid field).

Common form mistakes

Five patterns Sapota has seen on engagements:

1. Validation only on the client. Forms that pass without server validation. Trivially bypassed. Always validate server-side.

2. Errors aggregated at the top of the page. A user types a 50-character email, gets to the bottom of the form, hits submit, sees "Errors above" but cannot find which field. Inline errors next to the field.

3. Resubmission on back navigation. Form posts directly to a non-redirect controller. Browser back replays the post. Charges customers twice or creates duplicate records. Use post-redirect-get.

4. Hardcoded error messages. A form that bypasses the resource framework and renders hardcoded English error text. Localization broken. Move to Resource.msg().

5. CSRF tokens missing or static. A form without CSRF or with a static token. Vulnerable to attack. Use the framework's CSRF include.

What good form engineering looks like

A B2C Commerce site with healthy form discipline:

  • Form definitions in XML, validation rules colocated with field definitions.
  • Server-side validation comprehensive; client-side enhancement only.
  • Inline error messages next to their fields.
  • Post-redirect-get on every state-changing form.
  • CSRF tokens on every form.
  • Resource files for all user-facing strings.
  • Accessibility verified (labels, aria-invalid, focus management).

Forms are the most-interacted-with components on a B2C site. The framework is sufficient when used as intended. Sapota's Salesforce team holds the line on these patterns during code review because forms with subtle bugs produce customer service tickets for months.


Auditing or building forms in a B2C Commerce SFRA site? Sapota's Salesforce team, certified on B2C Commerce Developer (Comm-Dev-101), handles form architecture, validation strategy, and accessibility on production engagements. Get in touch ->

See our full platform services for the stack we cover.

Contact Us Now

Share Your Story

We build trust by delivering what we promise – the first time and every time!

We'd love to hear your vision. Our IT experts will reach out to you during business hours to discuss making it happen.

WHY CHOOSE US

"Collaborate, Elevate, Celebrate where Associates - Create Project Excellence"

SapotaCorp beyond the IT industry standard, we are

  • Certificated
  • Assured quality
  • Extra maintenance

Tell us about your project