From b2c
Creates storefront forms with validation in B2C Commerce using SFRA patterns—defines form fields in XML, handles submission in controllers, renders in ISML templates, and adds validation rules.
How this skill is triggered — by the user, by Claude, or both
Slash command
/b2c:b2c-formsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill guides you through creating forms with validation in Salesforce B2C Commerce using the SFRA patterns.
This skill guides you through creating forms with validation in Salesforce B2C Commerce using the SFRA patterns.
B2C Commerce forms consist of three parts:
Forms are defined in the cartridge's forms directory:
/my-cartridge
/cartridge
/forms
/default # Default locale
profile.xml
contact.xml
/de_DE # German-specific (optional)
address.xml
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.demandware.com/xml/form/2008-04-19">
<field formid="email" label="form.email.label" type="string"
mandatory="true" max-length="50"
regexp="^[\w.%+-]+@[\w.-]+\.\w{2,6}$"
parse-error="form.email.invalid"/>
<field formid="password" label="form.password.label" type="string"
mandatory="true" min-length="8" max-length="255"
missing-error="form.password.required"/>
<field formid="rememberMe" label="form.remember.label" type="boolean"/>
<action formid="submit" valid-form="true"/>
<action formid="cancel" valid-form="false"/>
</form>
| Type | Description | HTML Input |
|---|---|---|
string | Text input | <input type="text"> |
integer | Whole number | <input type="number"> |
number | Decimal number | <input type="number"> |
boolean | Checkbox | <input type="checkbox"> |
date | Date value | <input type="date"> |
| Attribute | Purpose | Example |
|---|---|---|
formid | Field identifier (required) | formid="email" |
label | Resource key for label | label="form.email.label" |
type | Data type (required) | type="string" |
mandatory | Required field | mandatory="true" |
max-length | Max string length | max-length="100" |
min-length | Min string length | min-length="8" |
regexp | Validation pattern | regexp="^\d{5}$" |
| Attribute | When Triggered |
|---|---|
missing-error | Mandatory field is empty |
parse-error | Value doesn't match regexp or type |
range-error | Value outside min/max range |
value-error | General validation failure |
See Form XML Reference for complete field attributes, groups, lists, and validation patterns.
'use strict';
var server = require('server');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');
server.get('Show',
csrfProtection.generateToken,
function (req, res, next) {
var form = server.forms.getForm('profile');
form.clear(); // Reset previous values
res.render('account/profile', {
profileForm: form
});
next();
}
);
module.exports = server.exports();
server.post('Submit',
server.middleware.https,
csrfProtection.validateAjaxRequest,
function (req, res, next) {
var form = server.forms.getForm('profile');
if (!form.valid) {
res.json({
success: false,
fields: getFormErrors(form)
});
return next();
}
// Access form values
var email = form.email.value;
var firstName = form.firstName.value;
// Process and save data
this.on('route:BeforeComplete', function () {
var Transaction = require('dw/system/Transaction');
Transaction.wrap(function () {
customer.profile.email = email;
customer.profile.firstName = firstName;
});
});
res.json({ success: true });
next();
}
);
function getFormErrors(form) {
var errors = {};
Object.keys(form).forEach(function (key) {
if (form[key] && form[key].error) {
errors[key] = form[key].error;
}
});
return errors;
}
server.get('Edit', function (req, res, next) {
var form = server.forms.getForm('profile');
form.clear();
var profile = req.currentCustomer.profile;
form.firstName.value = profile.firstName;
form.lastName.value = profile.lastName;
form.email.value = profile.email;
res.render('account/editProfile', { profileForm: form });
next();
});
<form action="${pdict.actionUrl}" method="POST" name="profile-form"
class="form-horizontal" data-action="${URLUtils.url('Profile-Submit')}">
<!-- CSRF Token -->
<input type="hidden" name="${pdict.csrf.tokenName}" value="${pdict.csrf.token}"/>
<div class="form-group ${pdict.profileForm.email.mandatory ? 'required' : ''}">
<label for="email" class="form-control-label">
${Resource.msg('form.email.label', 'forms', null)}
</label>
<input type="email"
id="email"
name="email"
class="form-control ${pdict.profileForm.email.error ? 'is-invalid' : ''}"
value="${pdict.profileForm.email.value || ''}"
<isif condition="${pdict.profileForm.email.mandatory}">required</isif>
maxlength="${pdict.profileForm.email.maxLength || 50}"/>
<isif condition="${pdict.profileForm.email.error}">
<div class="invalid-feedback">${pdict.profileForm.email.error}</div>
</isif>
</div>
<button type="submit" class="btn btn-primary">
${Resource.msg('button.submit', 'forms', null)}
</button>
</form>
Form labels and errors use resource bundles:
forms.properties:
form.email.label=Email Address
form.email.required=Email is required
form.email.invalid=Please enter a valid email address
form.password.label=Password
button.submit=Submit
forms_de_DE.properties:
form.email.label=E-Mail-Adresse
form.email.required=E-Mail ist erforderlich
route:BeforeComplete for database operationsFor comprehensive form patterns:
npx claudepluginhub salesforcecommercecloud/b2c-developer-tooling --plugin b2cCreates storefront controllers for Salesforce B2C Commerce using SFRA or classic patterns with server.get/post, middleware chains, and res.render/json.
Builds SFRA controllers for Salesforce B2C Commerce: server.get/post/use routes, middleware chains (append/prepend/replace), CSRF protection, form validation, rendering, and caching. Use for request handling.
Provides form UX best practices: inline validation with 'reward early, punish late' strategy, auto-save vs explicit save, error messages, multi-step wizards, toggles.