Framework7 Form Validation: Built-In Rules vs. Custom JavaScript
Decide between Framework7's declarative form validation and custom JavaScript validation with a comparison table, trade-off analysis, and a hybrid implementation that avoids duplicate error messages.
25 Nov 2025, 06:29 UTC

Every Framework7 app with a form eventually hits the same fork: do you lean on the framework's declarative validation (data attributes plus built-in rules), or write your own validation layer in JavaScript? The wrong choice shows up later as either duplicated error messages, untestable logic, or a validation system you have to rip out when requirements grow. This guide lays out the decision criteria, compares the two approaches, and shows a working hybrid pattern that avoids the common pitfalls.
The decision and its constraints
Framework7's built-in validation works through HTML data attributes on form inputs. You mark a field with rules like required, email, min, max, or a pattern regex, and the framework checks them on submit, displaying errors through its own toast and modal components. Custom validation means writing your own validate() function, calling it on submit (or on input change), and rendering errors yourself.
The real constraints that drive the choice:
- Rule complexity. Built-in rules cover single-field checks. Cross-field logic ("end date after start date", "at least one of phone or email") needs custom code either way.
- Async checks. Verifying a username against your API is not something declarative attributes can do.
- Testability. A standalone validation function can be unit-tested in Node without a DOM. Attribute-driven validation only runs inside a Framework7 page.
- UI consistency. Built-in validation reuses Framework7's toast/dialog styling, so errors look native to the app with zero extra work.
Side-by-side comparison
| Criterion | Built-in (data attributes) | Custom JavaScript |
|---|---|---|
| Setup effort | Minimal — attributes in markup | Write and wire up validate() yourself |
| Rule coverage | required, email, min, max, pattern, plus custom validators in app config | Unlimited, including async and cross-field |
| Error display | Automatic via Framework7 toasts/modals | You render messages yourself |
| Unit testing | Hard — needs a live Framework7 page | Easy — pure functions, testable in isolation |
| Reusability outside Framework7 | None | Portable to other frameworks or a backend |
| Performance risk | Low for typical forms | Runs on every input change unless you debounce |
| Maintenance burden | Low until requirements outgrow the rule set | Higher — you own the whole pipeline |
Trade-offs that matter in practice
Built-in validation wins when your form is a standard shape: registration, login, settings, feedback. You get consistent error UI for free and the markup documents the rules, which helps the next developer. The ceiling is real, though — the predefined rule set does not cover conditional requirements ("state is required only if country is US") or server-side uniqueness checks.
Custom validation wins when the form is the product: multi-step wizards, order forms with pricing rules, anything that talks to an API before accepting input. You also gain the ability to share validation logic with your backend or reuse it if you ever migrate off Framework7.
The trap is mixing both carelessly. If an input has data attributes and your custom validator also checks it, users can see two error messages for the same field — one from the framework's toast, one from your UI. Pick one owner per field.
Performance note: custom validators attached to input events fire on every keystroke. On large forms or with expensive checks (regex over long text, API calls), debounce the handler or validate only on change/submit.
A concrete hybrid implementation
A pragmatic pattern: use built-in attributes for simple per-field rules, and a single custom function for cross-field and async logic, run on submit. Each field has exactly one owner.
<!-- Built-in rules own the simple fields -->
<form id="signup-form" class="list form-validate">
<ul>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<input type="email" name="email" required validate
placeholder="Email" />
</div>
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<input type="password" name="password" required validate
minlength="8" placeholder="Password" />
</div>
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<!-- no validate attribute: custom code owns this field -->
<input type="password" name="confirm" placeholder="Confirm password" />
</div>
</div>
</li>
</ul>
<div class="block">
<button class="button button-fill" type="submit">Sign up</button>
</div>
</form>// Runs in your page init (e.g., pageInit for the route).
// Requires a Framework7 app instance; no special permissions needed.
app.on('pageInit', function (page) {
if (page.name !== 'signup') return;
var form = page.$el.find('#signup-form')[0];
form.addEventListener('submit', function (e) {
e.preventDefault();
// 1. Let Framework7 check the attribute-driven fields first.
var builtinValid = app.input.validateInputs(form);
if (!builtinValid) return; // framework shows its own errors
// 2. Custom cross-field check — the only owner of "confirm".
var data = app.form.convertToData(form);
if (data.password !== data.confirm) {
app.dialog.alert('Passwords do not match.', 'Sign up');
return;
}
// 3. All checks passed — submit to your API here.
});
});Two things to note. First, the confirm field deliberately has no validate attribute, so Framework7 never reports on it — no duplicate messages. Second, app.input.validateInputs() returns a boolean, which gives you a clean gate before running custom logic. Method names shown here reflect Framework7 v6/v7-era APIs; confirm against the docs for your installed version, since earlier releases differ.
How to verify the behavior
- Load the page, leave email empty, and submit. Expect a Framework7 error toast/highlight on the email field and no custom dialog.
- Enter a valid email and two different passwords. Expect only your custom "Passwords do not match" dialog — confirming the two systems are not double-reporting.
- Enter matching passwords and submit. Expect no error UI and your submit handler to run (add a temporary
console.logto confirm). - If you add input-event validation later, type quickly in a long form and watch for lag; if present, wrap the handler in a debounce.
Limitations
Built-in validation's error styling is tied to Framework7's components, so heavy visual customization may push you toward custom rendering anyway. Async built-in rules are not supported — any server round-trip check belongs in custom code. And because the exact validation API surface has shifted between major Framework7 versions, treat the method names above as a pattern to confirm against your installed version rather than copy-paste-guaranteed code.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.