Skip to main content





OutSystems Mobile Best Practices

  • Edit
    Collaborate with us
    Edit this page on GitHub
  • Mobile apps bring a completely new mindset for the development model. On the one hand, users have very high expectations regarding their experience with mobile apps. On the other hand, device limitations together with network restrictions can have a major impact on that experience. No matter the circumstances or limitations, users expect mobile apps to work like a charm!

    If you want to develop a mobile app that offers your users the best mobile experience, there are some key rules that you should always keep in mind:

    Focus on mobile use cases to guide your development

    Choose the best use cases for a great mobile experience. Start small and iterate. Use less and optimized content. Show what's more important first. Use common mobile patterns such as the ones provided by the OutSystems UI framework.

    Keep heavy logic on the server side

    Keep client-side information as simple as possible. Execute complex data processing and calculations on the server side, using the minimum number of server requests as possible. Cache your results on the client side.

    Choose OutSystems low-code over JavaScript

    OutSystems automatically optimizes the code for the best runtime performance. Use JavaScript only if it cannot be done with the OutSystems low-code language or in advanced extensibility scenarios.

    Test your app in real world scenarios

    Understand how your mobile app will be used and thoroughly test it before deploying to production. Assure that, device limitations, network restrictions, or offline periods will not prevent users having a great experience.

    Focusing on these ground rules, this article contains a set of best practices that will help you avoid common pitfalls which would impact user experience and the performance of your OutSystems mobile app. If you haven't done so already, you should learn how to develop high-quality mobile apps by following the Becoming a Mobile Developer online training first.

    We'll be updating and adding new content to this article along the way!

    User Interface

    A great UI experience increases the user perception of a fast and fluid mobile app. This section presents a set of best practices to improve the UI experience of your OutSystems mobile app.

    Discover and Use OutSystems Components

    Developing common mobile patterns from scratch takes a huge effort and feels like reinventing the wheel.


    Use the OutSystems UI framework or other OutSystems Forge components, which provide you with a large set of common mobile patterns. Remember to keep them up to date to benefit from the latest improvements.

    Design an Empty State for Content Being Fetched

    When a user navigates to a screen, the static content is usually rendered first, while the dynamic content takes more time since it is fetched asynchronously. This results in a poor UI experience, where an apparently incomplete screen is displayed with content moving around while the dynamic data is being rendered.


    Improve the user experience by designing and displaying an empty state image while the dynamic content is being fetched. A couple of good examples are Facebook or LinkedIn.

    image alt text

    This strategy is valid for all dynamic content in the screen such as blocks, cards or list items. When the empty state turns into the fetched content you may experience some flickering. To avoid flickering when the empty state turns into the fetched content, choose one image that assures a smooth transition, such as a blurred gray line or a spinner.

    Prioritize Screen Content Rendering

    By default, OutSystems mobile apps fetch screen data without a specific priority. If your screen has content that is more relevant and should be displayed first but you are not prioritizing its rendering, it may lead to a bad user experience. For example, displaying an advertising banner before the main information of the screen.


    Delay the rendering of the secondary content so that the main content is rendered first. To do this:

    1. Place the secondary content in a Block inside the True branch of an If widget. The Block must enclose all the logic to fetch the secondary content so that data fetching of the secondary content only runs when the Block is rendered.

    2. On the False branch of the If widget, place an empty state to avoid content from moving around when the secondary content is fetched.

    3. Set the If condition to a variable holding False by default.

    4. In the On Render event of the screen, add logic to set the variable to True so that the secondary content starts to render.

    image alt text

    Set the Width and Height of Image Widgets

    If you do not set the width and height of an Image widget, the user can get a flickering effect while the final image is being downloaded. For example, not setting the image height might cause the total height of the screen to change until the image loads completely since the widget height will be changing from 0px to the height of the final image.


    Set the width and height of the Image widget to the expected size of the final image.

    image alt text


    Handling data or implementing logic specifically for mobile scenarios can have a significant impact on a mobile app performance. Follow the best practices in this section to develop performant mobile apps. Also check the other sections for best practices that, although not directly related to performance, can contribute to improving it.

    Fix the Performance Warnings

    OutSystems automatically detects potential performance issues while you are developing your mobile app in Development Environment. Not paying attention to them may result in a bad performance of the application.

    image alt text


    Check the performance warnings displayed in the TrueChange™ tab and fix them.

    Design a Lightweight Local Storage

    Keeping a large amount of data in the device's local storage, for example in offline or cache scenarios, might become a performance killer in low-end devices.


    Use the following techniques to design a lightweight local storage:

    • Design the local storage data model using only the attributes that you need, instead of all attributes of the corresponding server entity. Consider denormalizing the local data model to avoid complex queries like having multiple JOINS. image alt text

    • Identify the best data synchronization patterns for your use case and define exactly when to run them.

    • Keep only the entity records you need for your use case in the local storage, instead of all records. For example, get only the open tasks for a given time period, instead of all tasks.

    • Consider synchronizing data in small chunks, starting with the most relevant data first (e.g. synchronize only text information first; photos and other images can be synchronized later).

    Keep the Splash Screen Simple and Fast

    When your mobile app starts, it displays a splash screen while internal operations are running. Adding heavy or lengthy operations to the splash screen will increase the time that the users must wait to use the app. Additionally, if the splash screen has a complex UI, the users may see a blank screen before the splash screen renders.

    image alt text


    To keep the splash screen simple and fast to load you should avoid:

    • Requests to the server

    • Heavy logic

    • Complex UI, namely too many Blocks

    For example, consider the following possible approaches:

    • Make server requests preferably in an event handler run at a later stage, like On Ready. If you really need to obtain server data in the app initialization stage, minimize the number of server calls (e.g. by obtaining several pieces of data using a single server action designed for the loading stage).

    • Do not perform lengthy operations when the mobile app starts. For example, if you must synchronize data that is used in the first screen of the app, keep the data transfer to a minimum and postpone the transfer of secondary data.

    Tip: To provide an amazing user experience, we also recommend that you customize the native splash screen.

    Optimize Fetching Server Data for a Screen

    Using the On After Fetch of server aggregates to execute other aggregates in sequence will generate several server requests and slow down the application.


    Create a Data Action containing aggregates in the correct order. This action will run in the server in a single request and return all data.

    image alt text

    Optimize the Loading of Lists

    Lists involve fetching and rendering multiple records at the same time. If not done carefully, the experience can become cumbersome, especially on low-end devices or with bad network connectivity.


    To get the most out of lists and provide a good experience, follow these rules:

    Fetch data on demand

    Fetch records as you need them instead of all at once. Start with a minimum set, for example, 10 records. As the user scrolls down, use the On Scroll Ending event to fetch the next set of records — for example, the following 10 records.

    To understand how this works, scaffold a list on a screen and this mechanism is provided by default.

    Keep list items simple

    Avoid designing list items with complex logic or complex widgets like JavaScript to load a map from Google Maps, for example. That complexity is multiplied by the number of items being rendered in the list.

    Avoid expanding content in list items

    Do not design list items with content that can be expanded, such as a description that is trimmed and has a 'Show All...' link. This will impact the behavior of the list while rendering. Use Silk UI patterns such as SplitScreen or MasterDetail instead.

    Fine tune how lists fetch data on demand

    Adjust the number of records that are initially loaded, the increment when scrolling down and the scroll threshold to trigger the On Scroll Ending event. It will provide a better user experience when using lists by avoiding visual glitches and slow list scrolls.

    The values to use depend on the size of the records:

    • The initial number of fetched items should ensure a balance between a fast data fetch and a sensible amount of scrolling until a request for more data occurs;

    • The incremental number of fetched items triggered by scrolling should generally be similar to the initial amount of fetched items, but you may need to tune it according to the usage of your app. If users will frequently use the list to search for entries, your app should be prepared to fetch data faster and load more items at a time;

    • The scroll threshold that triggers fetching new items is the distance in pixels before the scroll hits the end of the list and should be set to 2000 pixels. If you need to tune this threshold to improve the usability of your application, you can add the attribute infinite-scroll-threshold to the list widget with a new integer value in pixels.

    The next picture shows examples of values to use in different situations. You should start with these as initial guidelines and then test and adjust to your specific case.

    image alt text

    Provide visual feedback while fetching data on demand

    Users need a clear indication that the list has more items to show and that it is actually doing something. An animated image should be enough to provide this message to the user. Even so, as a performance target, keep in mind that during typical application usage the users should never hit a loading wall at the end of a list.

    Optimize the File Size of Images

    Using big image files in your mobile app can increase the download time (or even block the download) and lead to poor performance, especially in older devices or in low connectivity scenarios. They also consume storage space and might be too big for the screens of mobile devices, even high-DPI models.


    Reduce image size (in bytes) and adapt its dimensions (height/width) considering your target user's experience — follow Google's Image Optimization recommendations. Also, consider delaying the fetch of images to a later stage. Refer to the best practice to Prioritize Screen Content Rendering.


    OutSystems generates mobile apps with fast and optimized JavaScript code. You should carefully assess whether to add or not your own JavaScript code. To help you with that, follow these guidelines:

    Use OutSystems low-code instead of custom JavaScript

    The most common components for implementing a mobile app are provided in OutSystems UI and in the OutSystems Forge. You can probably find the ones you need for your scenario and customize them accordingly.

    Use JavaScript if strictly necessary and follow the best practices

    Keep in mind that OutSystems low-code is more performant but if you decide to use your own JavaScript, it's important that you read and follow the best practices in this section.

    Use the OutSystems public JavaScript API and not internal APIs

    Internal APIs are not supported and can change in any upgrade, thus breaking your JavaScript code.

    Follow the Industry JavaScript Best Practices

    When writing JavaScript, make sure you know the code you're implementing and how it affects the app.


    Follow the best practices for JavaScript development like the ones described in W3Schools or these JavaScript Coding Standards and Best Practices.

    For example, use performant JavaScript selectors to obtain elements from the DOM, and avoid properties and methods that trigger the browser to recalculate geometric information for the elements — known as Layout or Reflow.

    image alt text

    Avoid Using External JavaScript Libraries

    External JavaScript libraries can be too complex to run on mobile devices.


    Find OutSystems components in OutSystems UI or in the OutSystems Forge and use them instead of external JavaScript libraries.

    However, if it is clear that you really need to use the JavaScript library, use it in a Script element and not in JavaScript nodes.

    image alt text

    Using JavaScript code in Script elements allows:

    • A cleaner implementation of JavaScript libraries in your app.

    • Having a single source for each JavaScript library, thus avoiding multiple parses of the same JavaScript code.

    Refactor JavaScript Code into Client Actions

    Repeating the same JavaScript code in several JavaScript nodes increases maintainability efforts and makes your app more prone to errors.


    Encapsulate the repeated JavaScript code in a JavaScript node inside a client action. Call this client action whenever you need to run that JavaScript. This way, the JavaScript code is in a single place which improves the maintainability of the app.

    For example, encapsulate troubleshooting JavaScript functions such as console.log(), console.error() and alert() in client actions.

    image alt text

    Avoid Manipulating the DOM

    OutSystems mobile apps use React and manipulating the DOM with JavaScript can lead to unpredictable or undesirable behaviors.


    Do not use JavaScript that manipulates the DOM. This is only for advanced use cases.

    If you are implementing a known mobile pattern, look for it in OutSystems UI or the OutSystems Forge.

    image alt text

    Avoid Using Global Objects

    OutSystems uses single-page applications to optimize the runtime execution of mobile apps. As such, the window object isn't cleared between navigations and global objects are not destroyed, increasing memory usage in the long run.


    Avoid using objects that get attached to the window object, like global variables, cache objects, or listeners. Do the following instead:

    Change Widgets Style using OutSystems Low-code

    Using JavaScript to add/remove CSS classes to change the style of widgets may be a possible source of problems such as less code visibility and integrity, or flickering while rendering.


    Use the Style Classes property of widgets to change the CSS classes applied to them. In this property you can use expressions that are evaluated at runtime to change the styles dynamically.

    image alt text

    Use CSS for Animations instead of JavaScript

    JavaScript animations typically run on the device's Central Processing Unit (CPU) with direct impact on the mobile app performance.


    Use OutSystems low-code and style classes to run animations on the device's Graphics Processing Unit (GPU) and remove load from the CPU. For example, in Making Magic with WebSockets and CSS3, cards are animated using CSS.

    image alt text

    Learn more about how to properly animate elements in your mobile apps in Smooth as Butter: Achieving 60 FPS Animations with CSS3 and FLIP Your 60 FPS Animations, FLIP 'Em Good.

    However, if you really must use JavaScript, use the window.requestAnimationFrame() method to run the animation using the GPU.


    Understanding bugs in your mobile apps can sometimes be a hard task, but there are a few techniques that facilitate that troubleshooting process. Also following the development guidelines presented below allows you to more easily replicate and fix a problem in your mobile app.

    Define Fallbacks for Your Native Plugins

    When you are troubleshooting your app in a desktop browser, the native plugins will not be available. This might prevent you from troubleshooting issues detected in real mobile devices.


    Include sensible fallbacks in your apps to account for when the plugins are missing or do not work. This helps ensuring that apps can be tested using desktop browsers. You can do so by wrapping your plugin calls in client actions that return an error (or mock data for testing purposes instead) when a given plugin is not available.

    In the following example, the AddToContacts action of the Contacts native plugin was wrapped in a client action called AddToContacts_Safe that first validates if the plugin is available:

    image alt text

    Having these fallbacks in place also helps avoiding issues in the apps running in devices that do not support a given plugin.

    Create a Simple Client-Side Logging System

    Client-side issues can be hard to troubleshoot, especially when they seem to occur in a single user's device in production. If you cannot replicate the issues, it will be much difficult to find and fix them.


    OutSystems provides a way for you to log information in a mobile app. However, you can also create your client-side logging system that stores log entries in local storage. Then provide a way for the user to upload these logs in case of errors. This will allow you to analyze what might have caused an issue in that specific user's device.

    • Was this article helpful?