Skip to main content
RapidDev - Software Development Agency
outsystems-tutorial

Form Validation in OutSystems: Built-in and Custom Patterns

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.

What you'll learn

  • How the Form widget's Valid property works and why it matters
  • Setting up Mandatory validation on inputs without writing any logic
  • Writing custom validation logic in a Client Action using Valid and ValidationMessage
  • Combining built-in and custom rules in a single submit flow
  • Displaying user-friendly inline error messages with OutSystems UI feedback patterns
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read20-30 minOutSystems 11 and ODCMarch 2026RapidDev Engineering Team
TL;DR

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

1

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.

2

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.

3

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.

4

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.

typescript
1/* Expression for the If condition */
2Length(Trim(FullName)) < 2
3
4/* Assign node values */
5FullNameInput.Valid = False
6FullNameInput.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.

5

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'

typescript
1/* If condition for corporate domain check */
2not (Index(Email, "@company.com", 0, False, True) > 0)
3
4/* Assign node */
5EmailInput.Valid = False
6EmailInput.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.

6

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

ButtonRegisterOnClick_ClientAction_flow.txt
1/* Client Action: ButtonRegisterOnClick
2 Triggered by: Button 'Register' OnClick
3 Validation mode on button: Client & Server
4*/
5
6Start
7 |
8 v
9If: Length(Trim(FullName)) < 2
10 [True] --> Assign: FullNameInput.Valid = False
11 FullNameInput.ValidationMessage = "Name must be at least 2 characters"
12 |
13 v (falls through to Form.Valid check)
14 [False] --> (continues)
15 |
16 v
17If: not (Index(Email, "@company.com", 0, False, True) > 0)
18 [True] --> Assign: EmailInput.Valid = False
19 EmailInput.ValidationMessage = "Use your company email address"
20 [False] --> (continues)
21 |
22 v
23If: Form.Valid
24 [True] --> RegisterUser (Server Action)
25 | Input: FullName, Email, BirthDate
26 v
27 Message: "Registration successful!" (Success)
28 |
29 v
30 Destination: ConfirmationScreen
31 [False] --> End /* Errors already displayed inline */
32
33End

Common 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.

ChatGPT Prompt

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.

OutSystems Prompt

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.