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

Building Progressive Web Apps (PWAs) with OutSystems

To create a Progressive Web App in OutSystems, enable the 'Distribute as PWA' toggle in your Reactive Web app properties and configure the manifest with icons, theme color, and display mode. This tutorial covers the full PWA setup including install prompt testing with Lighthouse, service worker caching behavior, iOS install instructions, and the key limitation that PWAs cache assets but not data.

What you'll learn

  • Enable the PWA feature in an OutSystems Reactive Web app with a single toggle in app properties
  • Configure the web app manifest with app name, icons, theme color, and display mode
  • Test PWA installability using Chrome DevTools Lighthouse and the browser's install prompt
  • Understand what PWAs can and cannot do in OutSystems compared to native mobile apps
  • Customize the PWA service worker behavior for asset caching and offline asset delivery
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate11 min read30-45 minOutSystems 11 and ODCMarch 2026RapidDev Engineering Team
TL;DR

To create a Progressive Web App in OutSystems, enable the 'Distribute as PWA' toggle in your Reactive Web app properties and configure the manifest with icons, theme color, and display mode. This tutorial covers the full PWA setup including install prompt testing with Lighthouse, service worker caching behavior, iOS install instructions, and the key limitation that PWAs cache assets but not data.

PWAs in OutSystems: What Gets Generated for You

OutSystems Reactive Web apps can be converted to Progressive Web Apps with a single toggle. When enabled, OutSystems automatically generates a web app manifest and registers a service worker that pre-caches your app's static assets (HTML, CSS, JavaScript, images). This enables the browser's 'Add to Home Screen' install prompt and provides a native-app-like experience with a standalone window and no browser chrome. This tutorial covers enabling PWA, configuring the manifest, testing the install flow, and understanding the limitations — particularly that OutSystems PWAs use service worker caching for assets but do NOT provide offline data access (that requires Local Storage entities in a Mobile module).

Prerequisites

  • An OutSystems 11 Reactive Web app module open in Service Studio (PWA is for Reactive Web only, not Traditional Web or Mobile)
  • HTTPS configured on your environment (PWAs require HTTPS — localhost is exempt for testing)
  • App icons prepared: 192x192px and 512x512px PNG files

Step-by-step guide

1

Enable the PWA Toggle in App Properties

In Service Studio, look at the **Application Layer Tabs** on the right side. Click the **Interface tab**. At the top of the Interface tree, right-click your **application name** (the root node, not a module) → **Properties** to open the app properties panel. Alternatively, in the main editor, double-click on the app icon in the application toolbar at the top of Service Studio. In the **Properties panel** (right side), scroll down to the **Progressive Web App** section. Find the **Distribute as PWA** toggle → set it to **Yes**. Publish the module: click the **1-Click Publish** button (top center). After publishing, the app generates a manifest.json and registers a service worker.

Expected result: After publishing, navigating to your app in Chrome and opening DevTools → Application tab → Manifest shows the auto-generated manifest.json. The Service Worker section shows the registered OutSystems service worker.

2

Configure the App Manifest (Name, Icons, Theme Color)

With the PWA toggle enabled, go back to the **app Properties panel** in Service Studio. In the **Progressive Web App** section, configure: - **Application Name**: The full name shown on the home screen (e.g., `Inventory Manager`) - **Short Name**: Used when space is limited on home screen, max 12 characters (e.g., `Inventory`) - **Description**: Shown in some PWA install prompts - **Theme Color**: Hex color for the browser/status bar when the app is open (e.g., `#2C6ECB`) - **Background Color**: Splash screen background while app is loading (e.g., `#FFFFFF`) - **Display Mode**: Standalone (no browser chrome, most native-like), Minimal UI (shows URL bar), Browser (standard), or Fullscreen - **Application Scope**: The URL path scope — usually `/` for the full app **Add icons:** Interface tab → select your app root → find **App Icon** property → click to upload 192x192 PNG (required) and 512x512 PNG (required for splash screens). OutSystems generates the manifest iconList from these.

typescript
1/* Generated manifest.json (for reference — OutSystems creates this automatically) */
2{
3 "name": "Inventory Manager",
4 "short_name": "Inventory",
5 "description": "Manage your inventory offline and online",
6 "start_url": "/YourModule/",
7 "scope": "/YourModule/",
8 "display": "standalone",
9 "theme_color": "#2C6ECB",
10 "background_color": "#FFFFFF",
11 "icons": [
12 {
13 "src": "/YourModule/img/AppIcon_192.png",
14 "sizes": "192x192",
15 "type": "image/png"
16 },
17 {
18 "src": "/YourModule/img/AppIcon_512.png",
19 "sizes": "512x512",
20 "type": "image/png",
21 "purpose": "any maskable"
22 }
23 ]
24}

Expected result: DevTools → Application → Manifest shows your configured name, theme color, and both icons with their correct sizes. The Icons section shows green checkmarks if the icons are valid.

3

Test the PWA Install Flow in Chrome

To verify the PWA is installable, use Chrome on desktop or Android: **Desktop Chrome:** 1. Open your published app URL in Chrome 2. Look for the install icon in the address bar (computer screen with down arrow) 3. Click it → 'Install [App Name]' 4. The app opens in a standalone window without the browser address bar **Chrome DevTools Lighthouse test:** 1. Open DevTools (F12) → **Lighthouse** tab 2. Select **Progressive Web App** category → Generate Report 3. Review the PWA checklist — OutSystems should pass: HTTPS, manifest, service worker, icons, theme color 4. Common failure: 'Does not provide a valid apple-touch-icon' — add a 180x180 iOS icon **Android:** Open the app in Chrome for Android → three-dot menu → 'Add to Home Screen' → follow prompts

Expected result: Lighthouse PWA audit shows green (passed) for the core PWA checklist. The install prompt appears in Chrome. Installed app opens in standalone window with your configured theme color and no browser address bar.

4

Customize Service Worker Caching Strategy

OutSystems generates a service worker that uses a **network-first with cache fallback** strategy for app resources. When the device is offline, it serves the cached version of the app shell (HTML, CSS, JS). This means the app loads offline — but data is NOT available offline unless you also use Local Storage entities (Mobile module only). To add custom caching behavior, add a JavaScript file via **Interface tab → Scripts → Add Script** → name it `sw-custom.js`. Register it as a Required Script on the main screen. For custom caching of API responses, use the Workbox library or plain Cache API in a JavaScript node: **Note**: OutSystems' service worker is generated and may be overwritten on publish. Customizations must be applied through OutSystems' extension points, not by directly editing the generated service worker file.

typescript
1/* JavaScript node in a Client Action — demonstrate cache usage check */
2/* Checks if service worker is registered and active */
3var output = false;
4if ('serviceWorker' in navigator) {
5 navigator.serviceWorker.getRegistration().then(function(reg) {
6 if (reg && reg.active) {
7 $parameters.IsServiceWorkerActive = true;
8 }
9 });
10}
11
12/* Check if app is running in standalone (installed PWA) mode */
13var isStandalone = window.matchMedia('(display-mode: standalone)').matches
14 || window.navigator.standalone === true;
15$parameters.IsInstalledPWA = isStandalone;
16
17/* Show iOS install instructions only on iOS Safari in non-standalone mode */
18var isIOS = /iPhone|iPad|iPod/.test(navigator.userAgent);
19var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
20$parameters.ShowIOSInstallHint = isIOS && isSafari && !window.navigator.standalone;

Expected result: The service worker is active and caches app assets. The app shell loads in offline conditions (shows a loading state rather than browser's 'no connection' page). The iOS install hint displays for Safari users who haven't installed the app.

5

Handle PWA-Specific Behaviors: Back Button, Navigation, and Updates

PWAs running in standalone mode behave differently from browser tabs: **Back navigation**: There is no browser back button in standalone mode. Implement in-app back navigation using a Button or Link widget → Client Action → JavaScript node with `$public.Navigation.navigateBack()` or `history.back()`. **App updates**: When you publish a new version, the service worker detects the change and installs the new version in the background. Users see the update only after they close and reopen the PWA. To prompt immediate updates: In a JavaScript node on the main screen load: ```javascript navigator.serviceWorker.addEventListener('controllerchange', function() { window.location.reload(); }); ``` **Deep links**: PWAs support URL-based navigation. Use OutSystems' standard screen input parameters in URLs — they work the same in PWA mode as in browser mode. **Splash screen**: Defined by the background_color and 512x512 icon in the manifest. No additional configuration needed — the browser generates it automatically.

typescript
1/* Client Action: GoBack — add to all screens in PWA */
2/* Triggered by a back button widget */
3Start
4 JavaScript Node
5 /* Check if there's history to go back to */
6 var canGoBack = window.history.length > 1;
7 $parameters.HasHistory = canGoBack;
8 If HasHistory
9 [True]
10 Run JavaScript: $public.Navigation.navigateBack();
11 [False]
12 Navigate to Home screen
13 End
14
15/* Detect PWA install state — add to OnApplicationReady */
16Start
17 JavaScript Node
18 var isStandalone = window.matchMedia('(display-mode: standalone)').matches
19 || window.navigator.standalone === true;
20 $parameters.IsInstalledPWA = isStandalone;
21 If IsInstalledPWA
22 [True] SetClientVariable IsPWAMode = True
23 [False] SetClientVariable IsPWAMode = False
24 End

Expected result: Installed PWA shows correct back navigation on all screens. App update notifications are shown after a new version is published and the user has reopened the app. Deep links (shared URLs) open the correct screen in the installed PWA.

Complete working example

PWAHelpers.js
1/*
2 * OutSystems PWA Helper Scripts
3 * Add as a Script file: Interface tab Scripts Add Script
4 * Then add as Required Script on the main screen/layout block
5 */
6
7(function() {
8 'use strict';
9
10 // Detect install mode
11 window.OSPWAHelpers = {
12
13 // Is the app running as installed PWA (not in browser tab)?
14 isInstalledPWA: function() {
15 return window.matchMedia('(display-mode: standalone)').matches
16 || window.matchMedia('(display-mode: fullscreen)').matches
17 || window.navigator.standalone === true;
18 },
19
20 // Is the current device iOS (for custom install prompt)?
21 isIOS: function() {
22 return /iPhone|iPad|iPod/.test(navigator.userAgent)
23 && !window.MSStream;
24 },
25
26 // Is the current browser Safari (not Chrome on iOS)?
27 isSafari: function() {
28 return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
29 },
30
31 // Should we show the iOS manual install hint?
32 shouldShowIOSInstallHint: function() {
33 return this.isIOS() && this.isSafari() && !this.isInstalledPWA();
34 },
35
36 // Listen for service worker updates and reload automatically
37 setupAutoUpdate: function() {
38 if ('serviceWorker' in navigator) {
39 navigator.serviceWorker.addEventListener(
40 'controllerchange',
41 function() {
42 console.log('New service worker active — reloading for update');
43 window.location.reload();
44 }
45 );
46 }
47 },
48
49 // Check if service worker is registered and active
50 isServiceWorkerActive: function(callback) {
51 if ('serviceWorker' in navigator) {
52 navigator.serviceWorker.getRegistration().then(function(reg) {
53 callback(reg && reg.active ? true : false);
54 });
55 } else {
56 callback(false);
57 }
58 }
59 };
60
61 // Auto-setup on load
62 window.OSPWAHelpers.setupAutoUpdate();
63
64})();

Common mistakes

Why it's a problem: Enabling PWA on a Reactive Web module that is served over HTTP, causing the service worker to fail to register (PWA requires HTTPS).

How to avoid: Ensure your OutSystems environment enforces HTTPS. Configure IIS or your load balancer to redirect all HTTP requests to HTTPS before the app is served. Localhost is exempt for development.

Why it's a problem: Expecting the PWA to work fully offline for data, not understanding that the OutSystems service worker only caches static assets — data API calls still require network.

How to avoid: For offline data access, build a native Mobile app module with Local Storage entities and offline sync patterns. PWA provides offline asset caching (app shell), not offline data access.

Why it's a problem: Using the same icon file for both 192x192 and 512x512 sizes by scaling — small icons look blurry at 512x512 or details are lost at 192x192.

How to avoid: Design separate icon files optimized for each size, or use a vector source file exported at both resolutions. Include proper padding for maskable icon compliance.

Why it's a problem: Publishing a new app version and expecting installed PWA users to see it immediately — the old service worker continues serving cached assets until the app is closed and reopened.

How to avoid: Implement the 'controllerchange' service worker event listener that triggers a page reload when a new service worker takes control, or show a 'New version available — tap to refresh' notification using a FeedbackMessage.

Best practices

  • Always test PWA behavior on real mobile devices — Chrome DevTools simulation does not accurately replicate Android or iOS PWA install flows and service worker behavior.
  • Use the 'maskable' icon purpose on your 512x512 icon to ensure Android adaptive icons render correctly without awkward white borders.
  • Set background_color to match your app's primary background — this eliminates the flash of unstyled content during app launch from the home screen.
  • Keep the PWA scope at '/' only if the entire domain is your OutSystems app — if other apps share the domain, set scope to '/YourModule/' to prevent the service worker from intercepting requests for other apps.
  • Monitor service worker registration errors in the browser console — a failed service worker registration silently prevents installability without showing an error in the app.
  • Implement an 'offline fallback' screen that displays when the user navigates to a route that is not cached — without this, users see a white screen for uncached routes while offline.
  • For apps where offline data access is critical, complement PWA with a native Mobile app module using Local Storage entities — PWA service workers only cache static assets, not dynamic data.
  • In ODC, PWA configuration works identically — enable the toggle in ODC Studio app properties and the same manifest and service worker are generated.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I'm building an OutSystems Reactive Web app and want to make it installable as a PWA on Android and iOS. Explain: (1) where to find and enable the 'Distribute as PWA' toggle in Service Studio, (2) how to configure the manifest fields (name, short name, theme color, icons) and what sizes are required, (3) how to test the install prompt using Chrome Lighthouse, (4) how to detect whether the app is running in standalone PWA mode using a JavaScript node, and (5) how to show iOS users a manual install instruction. What are the limitations of OutSystems PWAs compared to native mobile apps?

OutSystems Prompt

In my OutSystems Reactive Web application in Service Studio, I need to enable PWA. Walk me through: the exact location of the 'Distribute as PWA' property (app properties, not module properties), how to add 192x192 and 512x512 icon images via the Interface tab, setting the theme color and display mode to standalone, publishing and verifying the manifest in Chrome DevTools, and adding a JavaScript node to detect iOS users who should see a manual 'Add to Home Screen' instruction.

Frequently asked questions

Can I add push notifications to an OutSystems PWA?

Push notifications in PWAs use the Web Push API and require a service worker. OutSystems' generated service worker supports this, but the backend push infrastructure (VAPID keys, push subscription storage, notification sending) must be implemented manually using JavaScript nodes and exposed REST APIs. Alternatively, use OneSignal or a similar push service that provides a JavaScript SDK you can integrate via a Script file.

Does enabling PWA affect my Reactive Web app for desktop users?

Enabling PWA adds a service worker and manifest — this has no negative effect on desktop browser users. Desktop Chrome users will also see the install prompt in the address bar, which they can ignore. The service worker may slightly improve repeat load performance by caching assets, but it does not change the UI or functionality for desktop users.

Can OutSystems PWAs access device features like the camera or GPS?

Yes, PWAs can access camera (via MediaDevices.getUserMedia), GPS (via Geolocation API), and some other hardware via web standard APIs. These require HTTPS and explicit user permission. The access is more limited than native Cordova/Capacitor plugins — for example, background GPS tracking and barcode scanning are not as reliable in PWA mode as in native mobile apps built with MABS.

What is the difference between enabling PWA in a Reactive Web module vs building a native Mobile app module in OutSystems?

A Reactive Web module with PWA enabled creates an installable web app running in a browser engine — no app store submission, instant updates, and broad cross-platform compatibility, but limited device access and no background processes. A Mobile module creates a native app compiled by MABS — supports Cordova/Capacitor plugins for full device access, Local Storage for offline data, and app store distribution, but requires build time and app store review for updates.

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.