what

Validation

Declare validation rules as HTML attributes. The framework handles both client-side and server-side validation automatically.

Enabling Validation

Add the w-validate attribute to any form to activate validation:

<form w-action="create" w-store="users" w-validate>
  <input name="email" type="email" w-required w-type="email">
  <button type="submit">Sign Up</button>
</form>

When the engine renders this form, it scans each input for w-* validation attributes, serializes the rules as a JWT, and injects a hidden <input name="w-rules"> field. On submission, the server decodes and validates against those signed rules.

Required Fields

The w-required attribute marks a field as mandatory. The engine also injects the standard HTML5 required attribute for browser-native validation.

<input name="username" w-required>
<input name="email" w-required w-type="email">
<textarea name="bio" w-required></textarea>

Type Checks

Use w-type to validate the format of a field's value:

<input name="email" w-type="email">
<input name="website" w-type="url">
<input name="age" w-type="number">
<input name="phone" w-type="phone">
<input name="birthday" w-type="date">
<input name="start_time" w-type="time">
TypeValidatesExample Match
emailStandard email format[email protected]
urlValid URL with protocolhttps://example.com
numberNumeric value42, 3.14
phonePhone number (digits, spaces, dashes, parens)+1 (555) 123-4567
dateISO date format2025-03-15
time24-hour time format14:30, 09:15:00

Length Limits

Enforce minimum and maximum character length with w-min and w-max. The engine also injects minlength and maxlength HTML5 attributes.

<input name="username" w-required w-min="3" w-max="20">
<textarea name="bio" w-max="500"></textarea>

Pattern Matching

Use w-pattern to validate against a regular expression:

<!-- US zip code -->
<input name="zip" w-pattern="^\d{5}$">

<!-- Two or three uppercase letters -->
<input name="code" w-pattern="[A-Z]{2,3}">

<!-- Slug format -->
<input name="slug" w-pattern="^[a-z0-9]+(-[a-z0-9]+)*$">

Field Matching

Use w-match to require that one field's value equals another field's value. Common for password confirmation:

<input name="password" type="password" w-required w-min="8">
<input name="confirm" type="password" w-required w-match="password">

The w-match value is the name of the field to match against.

Uniqueness

Check that a value does not already exist in a collection with w-unique:

<input name="email" w-required w-type="email" w-unique="users.email">
<input name="slug" w-required w-unique="posts.slug">

The format is collection.field. On server-side validation, the framework queries the database to ensure no existing record has the same value.

Tip: Uniqueness is only checked server-side. The client-side validation step skips this rule since it requires a database query.

Custom Error Messages

Override the default error message for any field with w-error:

<input name="email" w-required w-type="email"
       w-error="Please enter a valid email address">

<input name="zip" w-pattern="^\d{5}$"
       w-error="Invalid zip code">

The custom message replaces all validation messages for that field. If multiple rules fail, this single message is shown.

Error Display

When validation fails, the framework redirects back to the form with errors and previously submitted values available as template variables.

VariableDescription
#errors.fieldname#Error message for a specific field
#has_errors#true if any validation errors exist
#old.fieldname#Previously submitted value (form repopulation)
Registration form with error display
<form w-action="create" w-store="users" w-validate w-redirect="/welcome">

  <div class="form-group">
    <label>Username</label>
    <input name="username" value="#old.username#"
           w-required w-min="3" w-max="20">
    <if errors.username>
      <span class="text-danger text-sm">#errors.username#</span>
    </if>
  </div>

  <div class="form-group">
    <label>Email</label>
    <input name="email" value="#old.email#"
           w-required w-type="email" w-unique="users.email"
           w-error="This email is already taken or invalid">
    <if errors.email>
      <span class="text-danger text-sm">#errors.email#</span>
    </if>
  </div>

  <div class="form-group">
    <label>Password</label>
    <input name="password" type="password" w-required w-min="8">
    <if errors.password>
      <span class="text-danger text-sm">#errors.password#</span>
    </if>
  </div>

  <div class="form-group">
    <label>Confirm Password</label>
    <input name="confirm" type="password" w-required w-match="password">
    <if errors.confirm>
      <span class="text-danger text-sm">#errors.confirm#</span>
    </if>
  </div>

  <button type="submit" class="btn btn-primary">Create Account</button>
  <button type="reset" class="btn btn-outline">Cancel</button>
</form>

How It Works

Validation operates in two layers:

1. Client-side (on blur + submit)

The what.js client script decodes the JWT payload from the hidden w-rules field (base64, no secret needed for reading). On each blur event and on form submit, it checks the rules and displays error messages inline. This gives instant feedback without a server round-trip.

2. Server-side (before action)

On form submission, the action handler decodes the JWT using the server's secret to verify the rules have not been tampered with. It then validates all submitted fields against the signed rules. If validation fails:

  • Errors are stored in the flash session as #errors.fieldname#
  • Submitted values are preserved as #old.fieldname#
  • The #has_errors# flag is set to true
  • The user is redirected back to the form
Security: Validation rules are JWT-signed. Even if a user tampers with the hidden field in the browser, the server rejects any modified rules because the signature will not match.

Attribute Reference

AttributePurposeExample
w-validateEnable validation on a form<form w-validate>
w-requiredField must not be empty<input w-required>
w-typeFormat validationw-type="email"
w-minMinimum character lengthw-min="3"
w-maxMaximum character lengthw-max="100"
w-patternRegex pattern matchw-pattern="^\d{5}$"
w-matchMust equal another fieldw-match="password"
w-uniqueUniqueness check in databasew-unique="users.email"
w-errorCustom error messagew-error="Invalid input"