OutSystems form validation works through the Form widget's built-in Valid state combined with per-input Mandatory and data-type checks. For custom rules, set InputName.Valid = False and InputName.ValidationMessage in a Client Action before checking Form.Valid. This gives you full control over every error message shown to users.
Form Validation in OutSystems
Every OutSystems Reactive app needs forms, and every form needs validation. OutSystems provides a two-layer system: built-in checks (data type and Mandatory) that run automatically, and a runtime layer where you set Valid and ValidationMessage on individual inputs inside a Client Action. This tutorial walks through both layers for a typical registration form with name, email, and date-of-birth fields.
Prerequisites
- Service Studio installed and connected to a Personal or Development environment
- A Reactive Web App created (even an empty one is fine)
- Basic familiarity with adding widgets to a screen in Service Studio
Step-by-step guide
Add a Form widget and bind input variables
Add a Form widget and bind input variables
Open your screen in the Screen Editor. In the Toolbox (left panel), search for 'Form' and drag it onto the canvas. Inside the Form, drag three Input widgets from the Toolbox. Select each Input → in the Properties Panel (right side), set the Variable property: first Input → create a new Local Variable 'FullName' (Text), second → 'Email' (Email type), third → 'BirthDate' (Date). The Email data type gives you built-in format validation for free. The Date type enforces date format automatically.
Expected result: You have a Form widget containing three bound inputs. TrueChange should show no errors at this point.
Enable Mandatory validation on required fields
Enable Mandatory validation on required fields
Select the FullName Input widget. In the Properties Panel, find the Mandatory property and set it to True. Repeat for the Email Input. Leave BirthDate as non-mandatory for now. When a user submits without filling a Mandatory field, OutSystems automatically sets that input's Valid property to False and displays a default 'Required field.' message below the input. No Client Action logic is needed for this basic check.
Expected result: The FullName and Email inputs now have a red asterisk indicator in the live preview, and submitting empty fields shows the built-in error message.
Add a Submit button and set its Validation mode
Add a Submit button and set its Validation mode
Drag a Button widget from the Toolbox into the Form. In the Properties Panel, set the Button's Label to 'Register'. Find the Validation property (under the Events section in the Properties Panel) and set it to 'Client & Server'. This tells OutSystems to run client-side validation first (Mandatory checks, type checks) before calling any server logic. Next, right-click the Button → 'Add OnClick Client Action'. Service Studio creates a new Client Action and opens it in the Action Flow Editor.
Expected result: A Client Action named 'ButtonRegisterOnClick' is created and open in the Action Flow Editor.
Write custom validation logic in the Client Action
Write custom validation logic in the Client Action
In the Action Flow Editor, you should see Start → End. Add your custom checks before calling any server logic. Action flow: Start → If (Length(Trim(FullName)) < 2) → [True] Assign (set FullName.Valid = False, FullName.ValidationMessage = 'Name must be at least 2 characters') → [False path continues] → If (Form.Valid) → [True] call RegisterUser Server Action → [False] End. To add the Assign node: drag Assign from the Toolbox into the flow. In the Assign Properties, click '+' to add assignments: Variable = FullNameInput.Valid, Value = False. Add a second row: Variable = FullNameInput.ValidationMessage, Value = 'Name must be at least 2 characters'. The key pattern is: set individual input Valid/ValidationMessage first, then gate on Form.Valid — OutSystems automatically makes Form.Valid false when any child input is invalid.
1/* Expression for the If condition */2Length(Trim(FullName)) < 234/* Assign node values */5FullNameInput.Valid = False6FullNameInput.ValidationMessage = "Name must be at least 2 characters"Expected result: Clicking Register with a single-character name shows your custom error message inline below the name input.
Add a regex-based email format check
Add a regex-based email format check
After the name length check, add another If node in the action flow. Condition: use the built-in OutSystems expression to check the email format. OutSystems does not have a built-in Regex() function exposed in expressions, but you can use the EmailValidator pattern from Forge, or rely on the Email data type which enforces basic format. For a custom rule, use a JavaScript node to call a regex. Alternatively, the cleanest OutSystems-native approach is: set the Email input's data type to 'Email' (which enforces RFC-basic format automatically) and add an additional custom If check for your business rule, e.g. corporate email domain only: If (not (Index(Email, "@company.com", 0, False, True) > 0)) → Assign EmailInput.Valid = False, EmailInput.ValidationMessage = 'Use your company email address'
1/* If condition for corporate domain check */2not (Index(Email, "@company.com", 0, False, True) > 0)34/* Assign node */5EmailInput.Valid = False6EmailInput.ValidationMessage = "Use your company email address"Expected result: A non-company email triggers the custom inline error. The standard Email type validation still catches malformed addresses before your custom check runs.
Show a success Feedback Message on valid submission
Show a success Feedback Message on valid submission
After the If (Form.Valid) true branch, add a Message node (drag from Toolbox) to give the user confirmation, then optionally navigate away. Action flow for the True branch: → call RegisterUser Server Action → Message ('Registration successful!', MessageType Entities.MessageType.Success) → Destination node to navigate to a confirmation screen. If you prefer a toast notification: use the built-in feedback from the screen's OnNotification handler or use $public.FeedbackMessage.showFeedbackMessage() via a JavaScript node.
Expected result: A successful submission shows a green success message or navigates to the confirmation screen. The Form is reset or cleared depending on the navigation.
Complete working example
1/* Client Action: ButtonRegisterOnClick2 Triggered by: Button 'Register' OnClick3 Validation mode on button: Client & Server4*/56Start7 |8 v9If: Length(Trim(FullName)) < 210 [True] --> Assign: FullNameInput.Valid = False11 FullNameInput.ValidationMessage = "Name must be at least 2 characters"12 |13 v (falls through to Form.Valid check)14 [False] --> (continues)15 |16 v17If: not (Index(Email, "@company.com", 0, False, True) > 0)18 [True] --> Assign: EmailInput.Valid = False19 EmailInput.ValidationMessage = "Use your company email address"20 [False] --> (continues)21 |22 v23If: Form.Valid24 [True] --> RegisterUser (Server Action)25 | Input: FullName, Email, BirthDate26 v27 Message: "Registration successful!" (Success)28 |29 v30 Destination: ConfirmationScreen31 [False] --> End /* Errors already displayed inline */3233EndCommon mistakes
Why it's a problem: Placing validation logic in a Server Action instead of a Client Action
How to avoid: UI validation (setting Valid/ValidationMessage on inputs) must be in a Client Action because Server Actions cannot access screen widget runtime properties. Move validation logic to the OnClick Client Action.
Why it's a problem: Checking Form.Valid before setting individual input Valid properties
How to avoid: OutSystems only sets Form.Valid = False after you have set at least one child input's Valid = False or the built-in Mandatory check fires. Always run your custom assignments first, then evaluate If (Form.Valid).
Why it's a problem: Not resetting Valid = True before re-running validation on the second submit attempt
How to avoid: If a user fixes an error and resubmits, the previously-set Valid = False is still in memory. Add Assign nodes at the top of the Client Action to reset all inputs to Valid = True before running your checks.
Why it's a problem: Using a Button outside the Form widget
How to avoid: A Button must be a child element of the Form widget for the Validation property (Client & Server) to work. Drag the Button inside the Form in the Widget Tree.
Best practices
- Always wrap inputs inside a Form widget — without it, Form.Valid is never populated and validation cannot gate the submit flow.
- Run Client Actions for pure UI validation (no DB reads) and Server Actions only for business rules that require data lookups.
- Set ValidationMessage to a human-readable string that explains what is wrong and how to fix it, not just 'Invalid input'.
- Use the Email data type on email inputs to get free format validation before applying any custom domain or business rule checks.
- Never rely solely on client-side validation for security — always re-validate on the server in your RegisterUser Server Action.
- After a successful submit, either clear the form or navigate away to prevent accidental double-submissions.
- Check the TrueChange tab after adding validation logic — it highlights missing mandatory assignments and type mismatches immediately.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a form in OutSystems 11 Reactive Web. Explain how to implement custom validation in a Client Action. I have a Form widget with three Input widgets bound to variables FullName (Text), Email (Email type), and BirthDate (Date). I want to check that FullName has at least 2 characters and that the email belongs to @company.com. Show me the exact action flow pattern using OutSystems expression syntax and Assign nodes.
Add form validation to my OutSystems registration screen. The Form has inputs for FullName (Text, mandatory, min 2 chars), Email (Email type, must end in @company.com), and BirthDate (Date, optional). Create a ButtonRegisterOnClick Client Action that validates both fields, sets ValidationMessage on failure, checks Form.Valid, and calls a RegisterUser Server Action on success. Use OutSystems expression syntax throughout.
Frequently asked questions
Can I validate a form field on blur (when the user leaves the field) rather than on submit?
Yes. Add an OnChange event handler to the Input widget (Properties Panel → Events → On Change). In that Client Action, run your validation logic for just that field and set its Valid/ValidationMessage properties. This provides real-time inline validation as the user types or moves between fields.
Why does Form.Valid stay True even after I set an input's Valid to False?
Form.Valid is recalculated at runtime based on all child inputs. If you set InputName.Valid = False in an Assign node but then immediately check Form.Valid in the same Assign node, the recalculation may not have propagated yet. Place your Assign nodes before the If (Form.Valid) node in the action flow, not inside the same Assign block.
How do I show a summary of all errors at the top of the form instead of inline?
Create a Local Variable 'ErrorMessages' of type Text. In your validation Client Action, concatenate error descriptions into this variable. Add an If widget on the screen bound to Length(ErrorMessages) > 0 that shows a Container with an Expression widget displaying ErrorMessages. Update the variable in the same OnClick Client Action.
Does this validation pattern work the same way in ODC?
Yes. Form validation using Valid, ValidationMessage, and Client Actions is identical in ODC Studio. The only difference is that ODC apps have no modules — your Client Actions live directly under the app's Interface tab screens, not inside a module. The expression syntax and widget properties are unchanged.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation