OutSystems provides two approaches for modal dialogs: the built-in Popup widget (simplest, drag-and-drop) and the OutSystems UI Modal/Popup_Editor pattern from the Forge (more flexible, with overlay and close button). Open and close both using a Client Action that sets a Boolean local variable, which the dialog's IsOpen parameter is bound to.
Modals, Popups, and Confirmation Dialogs
Modal dialogs are essential for forms, confirmations, and detail views. OutSystems offers multiple approaches: the native Popup widget for simple cases, and the OutSystems UI Popup_Editor Block for production-quality modals with full overlay, accessible close behavior, and styling. Both work on the same principle — a Boolean local variable controls visibility, and a Client Action toggles it. This tutorial builds both types, covering data passing, close handling, and confirmation patterns.
Prerequisites
- A Reactive Web application open in Service Studio with at least one screen
- OutSystems UI Forge component installed (Trusted — comes pre-installed in most Reactive Web apps from OutSystems)
- A local Boolean variable named 'ShowDeleteConfirm' added to your screen (right-click screen → Add Local Variable → Name: ShowDeleteConfirm, Type: Boolean, Default: False)
Step-by-step guide
Add a Popup Widget to the Screen
Add a Popup Widget to the Screen
Open your screen in the Screen Editor (Interface tab → UI Flows → MainFlow → double-click your screen). In the Toolbox (left panel), search for **Popup**. The built-in Popup widget appears in the widget list. Drag it onto the screen canvas. The Popup widget appears as a bordered box in the Screen Editor. Everything inside the Popup is its content — it will appear in a centered overlay when opened at runtime. **Configure the Popup properties (Properties panel with Popup selected):** - **Name:** PopupDeleteConfirm - **TriggerWidget:** Leave empty — you will control this programmatically - **IsOpen:** Click the expression editor → select your local variable `ShowDeleteConfirm` **Add content to the Popup:** 1. Inside the Popup, drag a Container → Style Classes: `"popup-content padding-m"` 2. Inside the Container, add: - Text widget: `"Are you sure you want to delete this item?"` - Button: Label 'Delete', Style 'Danger' - Button: Label 'Cancel', Style 'Neutral' The Popup is invisible by default (IsOpen = False). It becomes visible when ShowDeleteConfirm = True.
Expected result: A Popup widget appears on the screen canvas containing your confirmation message and two buttons. The Properties panel shows IsOpen bound to the ShowDeleteConfirm variable.
Create Open and Close Client Actions
Create Open and Close Client Actions
Add two Client Actions to the screen to open and close the popup: **OpenDeleteConfirm action:** 1. Right-click the screen node (in the Interface tab) → **Add Client Action** 2. Name it `OpenDeleteConfirm` 3. In the Action Flow Editor: Start → Assign → End 4. In the Assign node, set: `ShowDeleteConfirm` = `True` **CloseDeleteConfirm action:** 1. Right-click the screen → **Add Client Action** → Name: `CloseDeleteConfirm` 2. Action flow: Start → Assign → End 3. In the Assign node, set: `ShowDeleteConfirm` = `False` **Wire up the buttons:** For the Cancel button inside the Popup: - Select it → Properties → Events → OnClick → CloseDeleteConfirm For the Delete button inside the Popup: - Select it → Events → OnClick → New Client Action → Name: `ConfirmDelete` - Action flow: Start → [call your delete Server Action] → Assign (ShowDeleteConfirm = False) → Refresh Aggregate → End For the trigger button on the main screen (the 'Delete' button next to each record): - Events → OnClick → OpenDeleteConfirm
1/* OpenDeleteConfirm Client Action flow:2 Start3 → Assign: ShowDeleteConfirm = True4 → End56 CloseDeleteConfirm Client Action flow:7 Start8 → Assign: ShowDeleteConfirm = False9 → End1011 ConfirmDelete Client Action flow:12 Start13 → DeleteTask(TaskId: SelectedTaskId) [Server Action]14 → Assign: ShowDeleteConfirm = False15 → Refresh Data: GetTasks16 → End17*/Expected result: Clicking the Delete button on the main screen sets ShowDeleteConfirm = True, which triggers the Popup to open. Clicking Cancel sets it back to False, closing the Popup.
Pass Data into a Popup Using Local Variables
Pass Data into a Popup Using Local Variables
When a list has multiple rows and each row has a Delete button, you need to pass the specific record's Id to the confirmation popup before the user confirms deletion. **Pattern — store the selected record ID before opening the popup:** 1. Add a second local variable to the screen: `SelectedTaskId` (Type: Task Identifier, Default: NullIdentifier()) 2. Modify the OpenDeleteConfirm Client Action to accept the task ID: - Right-click OpenDeleteConfirm → Add Input Parameter → Name: `TaskId`, Type: Task Identifier - In the Assign node, add: `SelectedTaskId` = `TaskId` (store for later use) - Keep `ShowDeleteConfirm` = `True` 3. Update the Delete button in the List widget: - The List widget iterates over GetTasks.List. Each row's Delete button should call OpenDeleteConfirm. - Select the Delete button → Events → OnClick → OpenDeleteConfirm - Set the TaskId input parameter to: `GetTasks.List.Current.Task.Id` 4. In the ConfirmDelete action, use `SelectedTaskId` when calling the Server Action: - DeleteTask(TaskId: SelectedTaskId) This pattern — store selection in a local variable, open popup, use stored variable on confirm — is the standard way to pass context to popups in OutSystems.
1/* OpenDeleteConfirm Client Action:2 Input Parameter: TaskId (Task Identifier)34 Flow:5 Start6 → Assign7 SelectedTaskId = TaskId8 ShowDeleteConfirm = True9 → End1011 ConfirmDelete Client Action:12 Start13 → DeleteTask(TaskId: SelectedTaskId) [Server Action]14 → Assign: ShowDeleteConfirm = False15 → Refresh Data: GetTasks16 → If(DeleteTask.Success)17 [True] → SetFeedbackMessage("Task deleted", Success)18 [False] → SetFeedbackMessage("Error deleting task", Error)19 → End20*/Expected result: Clicking the Delete button in row 3 opens the popup with SelectedTaskId set to that row's Task.Id. The ConfirmDelete action deletes the correct task regardless of which row triggered the popup.
Use the OutSystems UI Popup_Editor Pattern for Production Dialogs
Use the OutSystems UI Popup_Editor Pattern for Production Dialogs
The OutSystems UI framework includes a **Popup_Editor** Block that provides a fully styled modal with: - Dark overlay behind the dialog - Accessible close (X) button and keyboard Escape support - Configurable title header - Scrollable content area - Proper focus management for accessibility **Adding the Popup_Editor Block:** 1. Ensure OutSystems UI dependency is referenced (Ctrl+Q → find 'OutSystemsUI' → add Popup_Editor Block) 2. In the Screen Editor, drag the **Popup_Editor** block from the Toolbox (find it under 'Interaction' or search 'Popup_Editor') 3. Place it anywhere on the screen canvas — its visual position on the canvas does not matter because it renders as an absolute-positioned overlay **Configuring Popup_Editor properties:** - **IsOpen** → expression: `ShowDeleteConfirm` - **Title** → `"Confirm Deletion"` - **EnterAnimation** → `EnterAnimation.EnterBottom` (or ScaleUp) - **UseSingleClickClose** → False (requires explicit close action) **Adding content inside Popup_Editor:** The Popup_Editor block has a Content placeholder. Drag your dialog content (Text, Buttons) into this placeholder. **Handling the OnClose event:** Popup_Editor exposes an `OnToggle` event. Set it to your CloseDeleteConfirm action — this fires when the user clicks the X button or Escape.
1/* Popup_Editor Block properties:2 IsOpen: ShowDeleteConfirm (local variable)3 Title: "Confirm Deletion"4 EnterAnimation: EnterAnimation.EnterBottom5 Events:6 OnToggle: CloseDeleteConfirm (Client Action)78 Inside the Content placeholder:9 ├─ Text: "Are you sure you want to delete '" + GetTask.Task.Title + "'?"10 ├─ Container (row, margin-top-m)11 │ ├─ Button: "Delete" → Style: Danger → OnClick: ConfirmDelete12 │ └─ Button: "Cancel" → Style: Neutral → OnClick: CloseDeleteConfirm13*/Expected result: The Popup_Editor renders as a professional modal overlay with title, close button, and keyboard Escape support. The OnToggle event fires when the user tries to close via the X button or Escape key.
Build a Form Popup for Inline Record Creation
Build a Form Popup for Inline Record Creation
A common pattern is a 'New Record' popup with a form, so users can create records without leaving the current screen. **Setup:** 1. Add a local variable: `ShowNewTaskPopup` (Boolean, Default: False) 2. Add a local variable: `NewTaskRecord` (Type: Task, for the form data) 3. Add a 'New Task' button on the screen: Events → OnClick → Client Action that sets ShowNewTaskPopup = True and resets NewTaskRecord to empty **Form inside Popup_Editor:** 1. Drag Popup_Editor onto the screen, bind IsOpen to ShowNewTaskPopup 2. Inside the Content placeholder, drag a **Form** widget 3. Inside the Form, add Input widgets: - Input: Variable → NewTaskRecord.Title, Mandatory: Yes - TextArea: Variable → NewTaskRecord.Description - Input (DateType): Variable → NewTaskRecord.DueDate 4. Add a 'Save' Button inside the Form → OnClick: SaveNewTask Client Action **SaveNewTask Client Action:** Start → If(Form.Valid) → [True] CreateTask (Server Action, passing NewTaskRecord) → Assign ShowNewTaskPopup = False → Refresh GetTasks aggregate → [False] End
1/* OpenNewTaskPopup Client Action:2 Start3 → Assign4 NewTaskRecord = Default.Task5 ShowNewTaskPopup = True6 → End78 SaveNewTask Client Action:9 Start10 → If(Form1.Valid)11 [True]12 → CreateTask(TaskRecord: NewTaskRecord)13 → Assign: ShowNewTaskPopup = False14 → Refresh Data: GetTasks15 → End16 [False]17 → End18*/Expected result: Clicking 'New Task' opens a modal form with empty fields. Filling in the title and clicking Save creates the record and refreshes the list, then closes the modal. The Cancel button closes without saving.
Complete working example
1/* OutSystems Popup Patterns Reference23=== PATTERN 1: Built-in Popup Widget ===45Local Variables:6 ShowPopup: Boolean = False78Client Actions:9 OpenPopup:10 Start → Assign ShowPopup = True → End1112 ClosePopup:13 Start → Assign ShowPopup = False → End1415Widget tree:16 Screen17 ├─ Button "Open" → OnClick: OpenPopup18 └─ Popup19 IsOpen = ShowPopup20 └─ [content]21 └─ Button "Close" → OnClick: ClosePopup2223=== PATTERN 2: OutSystems UI Popup_Editor ===2425Local Variables:26 ShowEditPopup: Boolean = False27 SelectedRecordId: Task Identifier = NullIdentifier()2829Client Actions:30 OpenEditPopup(TaskId: Task Identifier):31 Start32 → Assign33 SelectedRecordId = TaskId34 ShowEditPopup = True35 → Refresh Data: GetTaskById36 → End3738 CloseEditPopup:39 Start → Assign ShowEditPopup = False → End4041Widget tree:42 Screen43 └─ Popup_Editor44 IsOpen = ShowEditPopup45 Title = "Edit Task"46 OnToggle = CloseEditPopup47 └─ [Content placeholder]48 ├─ Form49 │ └─ [Input widgets bound to GetTaskById]50 └─ Buttons row51 ├─ Button "Save" → OnClick: SaveTaskEdit52 └─ Button "Cancel" → OnClick: CloseEditPopup5354=== EXPRESSION EXAMPLES ===5556/* Dynamic popup title */57If(SelectedRecordId = NullIdentifier(),58 "New Task",59 "Edit: " + GetTaskById.Task.Title)6061/* Show popup only if user has edit permission */62ShowEditPopup and CheckRole(Roles.Admin)63*/Common mistakes
Why it's a problem: Setting IsOpen on the Popup widget to a static expression (True/False) instead of a local variable
How to avoid: A static True makes the popup always visible; static False makes it never show. Always bind IsOpen to a Boolean local variable that your Client Actions toggle. The popup opens when the variable becomes True and closes when it becomes False.
Why it's a problem: Popup opens but data inside it shows empty or stale values
How to avoid: After setting the ShowPopup variable to True in your OpenPopup action, refresh the relevant Aggregate or Data Action that populates the popup's content. Place the Refresh Data node BEFORE the Assign ShowPopup = True step so data is ready when the popup renders.
Why it's a problem: Using the Popup widget's TriggerWidget property instead of a Client Action to control open/close
How to avoid: The TriggerWidget approach works for simple toggle cases but does not allow custom logic (like refreshing data or storing selected IDs). Use the IsOpen binding with a local variable for any popup that needs data or context passed into it.
Why it's a problem: Placing the Popup_Editor Block at a nested position inside the Widget Tree instead of directly on the screen
How to avoid: Place Popup_Editor as a direct child of the screen (not inside another container). Modal overlays render based on absolute positioning — being nested inside a container with overflow:hidden or a transform will clip the overlay. Place it at the top level of the Widget Tree.
Best practices
- Use a single Boolean local variable per popup to control visibility — keep the naming consistent: ShowXxxPopup
- Always reset form data when opening a 'New Record' popup — set the record variable to its default before setting ShowPopup = True
- Use the OutSystems UI Popup_Editor for production apps — it handles accessibility (focus trap, keyboard close) that the basic Popup widget does not
- Never nest popups (a popup that opens another popup) — this confuses users and creates complex state management; use a wizard pattern instead
- Handle the OnToggle event on Popup_Editor to close the popup when users press Escape or click the X — do not rely solely on button clicks
- Keep popup content lightweight — popups that load complex aggregates on open should use a loading spinner (Boolean IsLoading variable + If widget) to prevent blank flash
- For mobile apps, consider using a BottomSheet (OutSystems UI pattern) instead of a modal — bottom sheets are more ergonomic on small screens
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
In my OutSystems Reactive Web app, I have a Table widget showing a list of Tasks. Each row has a Delete button. When clicked, I want a confirmation modal to appear asking 'Are you sure you want to delete [Task Title]?' with Confirm and Cancel buttons. On Confirm, the task is deleted and the list refreshes. Show me: (1) the local variables needed, (2) the Client Action flows for Open, Close, and Confirm, and (3) the widget setup using OutSystems UI Popup_Editor.
I am using the OutSystems UI Popup_Editor block for a 'New Task' form modal. The modal has Input fields for Title and Description bound to a local NewTask variable. The Save button should validate the form, call a Server Action to create the task, close the modal, and refresh the list. Show me the complete SaveNewTask Client Action flow using OutSystems arrow notation and the correct form validation check.
Frequently asked questions
Can I have multiple different popups on the same screen?
Yes. Add one Boolean local variable per popup (e.g., ShowDeleteConfirm, ShowEditRecord, ShowNewTask) and one Popup or Popup_Editor widget for each. Each popup is controlled by its own variable. You can open only one at a time by ensuring other variables are False when opening a new one, or allow them to layer (unusual and generally not recommended for UX).
How do I prevent the popup from closing when the user clicks outside of it (the overlay)?
On the OutSystems UI Popup_Editor, set the UseSingleClickClose property to False. This disables the overlay click-to-close behavior, requiring users to use the X button or your explicit Close button. For the basic Popup widget, overlay click-close is not built in — it only closes when you set IsOpen to False via a Client Action.
How do I return data from a popup back to the parent screen?
Popups share the same screen scope as their parent — they are not separate screens. Local variables defined on the screen are accessible both in the parent screen logic and in event handlers of widgets inside the popup. To return data from a popup, have the popup's Save/Confirm action write to a screen-level local variable before closing. The parent screen reads that same variable after the popup closes.
What is the difference between a Popup widget and a Popup_Editor block from OutSystems UI?
The built-in Popup widget is a basic container widget that shows/hides based on IsOpen — it has minimal styling and no built-in overlay, close button, or accessibility features. The Popup_Editor is a pre-built Block from the OutSystems UI Forge component that adds a dark overlay, title bar, X close button, keyboard Escape support, and enter/exit animations. For production apps, use Popup_Editor. For quick prototyping or very simple cases, the basic Popup widget is faster to set up.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation