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
Enable the PWA Toggle in App Properties
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.
Configure the App Manifest (Name, Icons, Theme Color)
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.
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.
Test the PWA Install Flow in Chrome
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.
Customize Service Worker Caching Strategy
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.
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}1112/* Check if app is running in standalone (installed PWA) mode */13var isStandalone = window.matchMedia('(display-mode: standalone)').matches14 || window.navigator.standalone === true;15$parameters.IsInstalledPWA = isStandalone;1617/* 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.
Handle PWA-Specific Behaviors: Back Button, Navigation, and Updates
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.
1/* Client Action: GoBack — add to all screens in PWA */2/* Triggered by a back button widget */3Start4→ JavaScript Node5 /* Check if there's history to go back to */6 var canGoBack = window.history.length > 1;7 $parameters.HasHistory = canGoBack;8→ If HasHistory9 [True]10 → Run JavaScript: $public.Navigation.navigateBack();11 [False]12 → Navigate to Home screen13→ End1415/* Detect PWA install state — add to OnApplicationReady */16Start17→ JavaScript Node18 var isStandalone = window.matchMedia('(display-mode: standalone)').matches19 || window.navigator.standalone === true;20 $parameters.IsInstalledPWA = isStandalone;21→ If IsInstalledPWA22 [True] → SetClientVariable IsPWAMode = True23 [False] → SetClientVariable IsPWAMode = False24→ EndExpected 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
1/*2 * OutSystems PWA Helper Scripts3 * Add as a Script file: Interface tab → Scripts → Add Script4 * Then add as Required Script on the main screen/layout block5 */67(function() {8 'use strict';910 // Detect install mode11 window.OSPWAHelpers = {1213 // Is the app running as installed PWA (not in browser tab)?14 isInstalledPWA: function() {15 return window.matchMedia('(display-mode: standalone)').matches16 || window.matchMedia('(display-mode: fullscreen)').matches17 || window.navigator.standalone === true;18 },1920 // Is the current device iOS (for custom install prompt)?21 isIOS: function() {22 return /iPhone|iPad|iPod/.test(navigator.userAgent)23 && !window.MSStream;24 },2526 // Is the current browser Safari (not Chrome on iOS)?27 isSafari: function() {28 return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);29 },3031 // Should we show the iOS manual install hint?32 shouldShowIOSInstallHint: function() {33 return this.isIOS() && this.isSafari() && !this.isInstalledPWA();34 },3536 // Listen for service worker updates and reload automatically37 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 },4849 // Check if service worker is registered and active50 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 };6061 // Auto-setup on load62 window.OSPWAHelpers.setupAutoUpdate();6364})();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.
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?
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation