OutSystems has three ways to express conditional logic: the If widget on screens (shows/hides UI based on a Boolean expression), the If node in action flows (branches logic), and the If() function in expressions (inline ternary). The Switch node handles multiple-branch conditions in action flows, equivalent to a switch/case statement.
Conditional Logic in OutSystems
OutSystems provides conditional logic at three levels: the screen layout (If widget), the action flow (If node and Switch node), and inline expressions (If() function). Understanding which tool to use where — and the important OutSystems-specific syntax differences from JavaScript — eliminates the most common beginner mistakes and TrueChange errors.
Prerequisites
- Service Studio with a Reactive Web App open
- A screen with at least one Local Variable and one widget
- Familiarity with the Screen Editor and Action Flow Editor
Step-by-step guide
Use the If widget to conditionally show UI on a screen
Use the If widget to conditionally show UI on a screen
The If widget is a layout element that renders either its True branch or False branch based on a Boolean expression. It does NOT hide the element with CSS — it removes it from the DOM when False. In the Screen Editor, search the Toolbox for 'If' and drag it onto the canvas. In the Properties Panel: - Condition: enter your Boolean expression, e.g. User.IsLoggedIn The Widget Tree shows two branches inside the If widget: True Branch and False Branch. Drag UI elements into each branch. Only the branch matching the condition is rendered. Common use cases: - Show 'Admin' menu only if CheckAdminRole() = True - Show loading spinner while data fetches: GetEmployees.IsLoading - Hide empty state message: not GetEmployees.List.Empty - Conditional form sections: SelectedCountry = Entities.Country.US
1/* If widget condition examples */2User.IsLoggedIn3CheckAdminRole()4not GetEmployees.List.Empty5CurrentStep = 36Order.Status = Entities.OrderStatus.Pending7SelectedCountryId <> NullIdentifier()8GetOrders.IsLoadingExpected result: The True branch content is visible and the False branch is completely absent from the rendered HTML when the condition is False.
Use the If node in action flows for branching logic
Use the If node in action flows for branching logic
In the Action Flow Editor (for both Client and Server Actions), the If node creates a binary branch. Drag 'If' from the Toolbox onto the flow between two nodes. The If node has: - Condition (Boolean expression): set in Properties Panel - True connector (right side): flow when condition is True - False connector (bottom): flow when condition is False Example — save or navigate based on form validity: Start → If: Form.Valid → [True] SaveEmployee Server Action → Destination → [False] End Nesting: connect multiple If nodes in sequence. The False branch of one If can connect to the next If node for an else-if chain: Start → If: Status = 'Active' → [True] ... → [False] → If: Status = 'Pending' → [True] ... → [False] → end default case
1/* Action flow with nested If nodes */2Start3 --> If: Order.TotalAmount > 10004 [True] --> Assign: DiscountRate = 0.155 [False] --> If: Order.TotalAmount > 5006 [True] --> Assign: DiscountRate = 0.107 [False] --> Assign: DiscountRate = 0.058 --> Assign: FinalAmount = Order.TotalAmount * (1 - DiscountRate)9 --> EndExpected result: The action flow branches correctly at runtime. Set a breakpoint on the If node in the Debugger to verify which branch executes.
Use the If() function for inline conditional expressions
Use the If() function for inline conditional expressions
The If() function returns one of two values based on a condition. Use it anywhere an expression is expected — widget properties, Assign node values, Aggregate filter conditions, FormatDate arguments. Syntax: If(BooleanCondition, TrueValue, FalseValue) Critical rule: TrueValue and FalseValue MUST be the same data type. OutSystems does not auto-cast — passing a Text True value and an Integer False value causes a TrueChange type error. Examples: - If(Employee.IsActive, "Active", "Inactive") → Text - If(Order.Amount > 100, Order.Amount * 0.9, Order.Amount) → Decimal - If(User.Age >= 18, True, False) → same as just User.Age >= 18 - If(GetOrder.IsLoading, "Loading...", IntegerToText(GetOrder.List.Length) + " orders") → Text
1/* If() expression examples */23/* Text conditional */4If(Employee.IsActive, "Active", "Inactive")56/* Decimal conditional */7If(Order.Amount > 100, Order.Amount * 0.9, Order.Amount)89/* Nested If() - equivalent to else-if */10If(Age >= 65, "Senior", If(Age >= 18, "Adult", "Minor"))1112/* Used in FormatDate */13FormatDate(If(Order.ShippedDate = NullDate(), CurrDate(), Order.ShippedDate), "dd/MM/yyyy")1415/* Used in Aggregate filter */16Order.Status = If(ShowAllOrders, Entities.OrderStatus.All, Entities.OrderStatus.Pending)1718/* Conditional CSS class */19If(IsSelected, "selected active", "")Expected result: Expressions using If() compile without TrueChange errors. The Expression widget on screen dynamically shows 'Active' or 'Inactive' based on Employee.IsActive.
Use the Switch node for multi-branch conditions in action flows
Use the Switch node for multi-branch conditions in action flows
The Switch node is the OutSystems equivalent of a switch/case statement. Use it when you have three or more branches based on the same value. Drag 'Switch' from the Toolbox into an action flow. In the Properties Panel, add cases: - Click '+' for each case - Case 1: Expression = Order.Status = Entities.OrderStatus.Pending, connect to 'Send pending reminder' logic - Case 2: Expression = Order.Status = Entities.OrderStatus.Shipped, connect to 'Send tracking email' logic - Case 3: Expression = Order.Status = Entities.OrderStatus.Cancelled, connect to 'Send cancellation notice' logic - Otherwise (default): connect to logging or no-op End Switch evaluates cases top-to-bottom and takes the FIRST match. Unlike some languages, there is no fallthrough — only one branch executes. When to use Switch over nested Ifs: - 3+ branches on the same variable → Switch - 2 branches or unrelated conditions → If node chain
1/* Switch node cases - evaluates top to bottom, first match wins */2Case 1: Order.Status = Entities.OrderStatus.Pending3 --> Send pending reminder email4Case 2: Order.Status = Entities.OrderStatus.Shipped5 --> Send tracking number SMS6Case 3: Order.Status = Entities.OrderStatus.Cancelled7 --> Send cancellation refund notice8Otherwise:9 --> LogMessage: "Unhandled order status: " + Order.StatusExpected result: The action flow executes the matching case branch. Only one branch fires per execution regardless of how many cases exist.
Apply conditional logic to widget properties beyond Visible
Apply conditional logic to widget properties beyond Visible
The If widget handles conditional rendering, but you can also apply conditional logic to any widget property that accepts an expression: - Style Classes: If(IsHighlighted, "highlight-row", "") — applies a CSS class conditionally - Enabled: SelectedCountryId <> NullIdentifier() — disables a button until a precondition is met - Mandatory: If(RequiresPhone, True, False) — makes a field mandatory based on context - Value in Expression widget: If(Employee.IsActive, "Active", "Inactive") — text content For Button Enabled, set the Enabled expression in the Button's Properties Panel: Enabled = Form.Valid and SelectedCountryId <> NullIdentifier() This creates a reactive UI where the button only becomes clickable when the form is valid and a country is selected — no Client Action needed.
1/* Widget property conditional expressions */23/* Button Enabled */4Form.Valid and SelectedCountryId <> NullIdentifier()56/* Input Mandatory */7If(DeliveryMethod = Entities.DeliveryMethod.Home, True, False)89/* Dynamic CSS class */10If(Row.IsOverdue, "row-overdue text-danger", "")1112/* List item background color via Style Class */13If(Mod(GetEmployees.List.CurrentRowNumber, 2) = 0, "row-even", "row-odd")Expected result: Widget properties update reactively as the bound variables change, without requiring Client Action logic for simple show/disable/style patterns.
Complete working example
1/* ============================================================2 IF WIDGET PATTERNS (Screen Editor)3 ============================================================ */45If (Condition = User.IsAdmin)6 True Branch: Admin Dashboard Container7 False Branch: Standard User Dashboard Container89If (Condition = GetOrders.IsLoading)10 True Branch: Spinner widget11 False Branch: If (not GetOrders.List.Empty)12 True: Table widget bound to GetOrders.List13 False: BlankSlate 'No orders found'1415/* ============================================================16 IF NODE PATTERNS (Action Flows)17 ============================================================ */1819/* Client Action: ApplyDiscount */20Start21 --> If: Order.TotalAmount > 100022 [True] --> Assign: DiscountRate = 0.1523 [False] --> If: Order.TotalAmount > 50024 [True] --> Assign: DiscountRate = 0.1025 [False] --> Assign: DiscountRate = 0.0526 --> Assign: FinalAmount = Order.TotalAmount * (1 - DiscountRate)27 --> End2829/* ============================================================30 IF() EXPRESSION PATTERNS31 ============================================================ */3233/* Inline ternary for display */34If(Employee.IsActive, "Active", "Inactive")3536/* Nested If() for three outcomes */37If(Score >= 90, "A", If(Score >= 70, "B", If(Score >= 50, "C", "F")))3839/* Null safety pattern */40If(Order.ShippedDate = NullDate(), "Not yet shipped",41 "Shipped: " + FormatDate(Order.ShippedDate, "dd/MM/yyyy"))4243/* ============================================================44 SWITCH NODE PATTERN45 ============================================================ */46Switch on Order.Status:47 Pending --> send reminder48 Shipped --> send tracking info49 Delivered --> request review50 Cancelled --> process refund51 Otherwise --> log unknown statusCommon mistakes
Why it's a problem: Using If(condition, True, False) when just 'condition' works
How to avoid: If the TrueValue is True and FalseValue is False, the If() call is redundant. Use the Boolean expression directly: Button.Enabled = IsFormValid is cleaner than Button.Enabled = If(IsFormValid, True, False).
Why it's a problem: Mismatched types in If() expression (Text True value, Integer False value)
How to avoid: TrueChange will show 'Invalid data type' error. Ensure both values are the same type. Use IntegerToText() or TextToInteger() to convert. Example: If(IsNew, "New", IntegerToText(RecordCount)) — both are now Text.
Why it's a problem: Using a Screen Aggregate inside an If widget condition that runs before data loads
How to avoid: Use GetEmployees.IsLoading and GetEmployees.List.Empty to handle loading and empty states. Wrap the data-dependent If condition in an outer If checking IsLoading first to prevent accessing .List.Current on an empty list.
Why it's a problem: Placing the If node's False branch logic above the True branch in the flow visually
How to avoid: In Service Studio's Action Flow Editor, the True connector goes right and the False connector goes down by default. When arranging complex flows, keep the happy path going right and exception/alternative paths going down for readability — this matches the visual convention OutSystems developers expect.
Best practices
- Use the If widget (not CSS visibility) for conditional rendering of substantial UI sections — it removes the element from the DOM and avoids rendering overhead.
- Use Switch nodes when you have three or more branches on the same variable — it is more readable and maintainable than chained If nodes.
- Keep If() expression nesting to two levels maximum. Three or more nested If() calls should be moved to a Assign node in an action flow or a local Server Action function.
- Both values in an If() expression must be the same data type — use conversion functions (IntegerToText, TextToInteger) if needed.
- For widget Enabled and Mandatory properties, use direct Boolean expressions rather than If(condition, True, False) — If(x, True, False) is identical to just x.
- Add an Otherwise branch to every Switch node to handle unexpected values — log the unhandled case to Service Center for debugging.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Explain the three types of conditional logic in OutSystems: the If widget (screen layout), the If node (action flows), and the If() function (expressions). Show examples of each with OutSystems expression syntax. Include a Switch node example for multi-branch logic and explain the TrueValue/FalseValue type-matching rule in If() expressions.
Add conditional logic to my OutSystems employee list screen: (1) If widget showing a BlankSlate when GetEmployees.List.Empty is True, (2) If widget showing an 'Admin only' section when CheckAdminRole() is True, (3) Expression widget showing employee status as 'Active' or 'Inactive' using the If() function on Employee.IsActive, (4) Submit button Enabled property set to Form.Valid and SelectedDeptId <> NullIdentifier().
Frequently asked questions
What is the difference between the If widget and setting Visible = False on a widget?
The If widget completely removes the non-rendered branch from the DOM. Setting Visible = False hides the element but leaves it in the DOM. Use If widget for conditional content that should not be rendered at all (cleaner HTML, better performance). Use Visible = False for elements that need to toggle frequently with animation or when keeping the DOM position matters.
Can I use the Switch node in a Client Action, or only in Server Actions?
The Switch node is available in both Client Actions and Server Actions. Drag it from the Toolbox in the Action Flow Editor for either action type. The behavior is identical regardless of execution context.
How do I express an 'and' / 'or' condition in an OutSystems expression?
OutSystems uses lowercase 'and' and 'or' operators (not && or ||). Example: Employee.IsActive and Employee.DepartmentId = SelectedDeptId. For negation, use 'not': not Employee.IsActive. These are OutSystems expression keywords, not JavaScript operators.
Is there a null/nil check equivalent in OutSystems expressions?
Yes, but it depends on the type. For entity identifiers: use = NullIdentifier() or = NullTextIdentifier(). For dates: = NullDate(). For binary data: = NullBinaryData(). For text: = '' (empty string — OutSystems has no null text). For integers: = 0 (the default, not null). OutSystems intentionally avoids null for most types to prevent null reference exceptions.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation