This article guides you through the steps to create a reusable component in OutSystems. It includes recommendations, best practices, and information for common sccenarios.
Components are one of the most important aspects of low-code platforms, as they enable reusability of code and repositories of component libraries. As a result, developers can build high-quality app components and share them with others, boosting application development time and code fragmentation.
What's a component?
A component is a reusable object that speeds up application creation and delivery. It can be either an application or a module that provides additional features. It should be easy to use and understand, and it should focus on a common use case.
After you create a component, you can apply it in any of these ways:
Modularize your code to reuse within your application (applying the Don't Repeat Yourself principle - DRY).
Use it as an infrastructure piece for your factory, to reuse in more than one application.
Published it to the Forge, where it can serve multiple apps and developers.
The successful creation of a component encompasses:
Deciding its purpose.
Designing an API and logic that support the main scenarios.
Defining the presentation by giving it the right names and icons, for example.
Following a set of best practices that enhance the experience of reuse and maintenance.
Making sure you cover non-functional requirements, such as performance, security, and scalability.
Planning the release and letting your component see the light of day.
The most common component categories, which you can share in OutSystems Forge, are:
- Device capabilities
- User Interface
- Functional libraries & utilities
- Development tools
See how you can use a component made by the community.
Creating a component: Planning it
Before you start building a component, plan what you want it to do. For example, what options should it have? What should the default behavior be? What are the most important use cases and how should your component address them?
A component is usually a set of blocks and actions that you distribute as a module and/or an application. For example, if you want to modularize your own code, your component would most likely be a new block. If you're planning to release it to your factory or publicly, your component should be a module for use inside an application.
If you're creating a component that contains only logic, consider creating an empty module so it doesn't contain UI dependencies. This approach makes your component lightweight and simpler to reuse.
Alternatively, if your component contains UI elements, start by creating a module with a template. This approach provides you with an optimal structure built for that purpose.
Consider your scenarios to build successfully from the start.
Creating a component: Building it
After defining the purpose of your component, you can start to build it by defining any relevant UI elements, actions, and logic.
Keep in mind that your components must fit into the Architecture Canvas methodology used to build OutSystems applications.
Take advantage of modern web features using Reactive Web
Reactive Web Apps are high performance and scalable. Building Reactive Web Apps boosts the development experience across web and mobile.
When building web components, promote Reactive Web over Traditional Web.
API design to offer the best options
There's no point to owning a great car if you don't know how to drive it. The same applies to a component. During implementation, keep in mind the developer experience and skill set. As such:
Keep the API small in scope and simple to understand. For example, instead of offering a 360-degree input for alignment, simply offer top, bottom, right, and left.
Focus the API on the main use cases and offer the best defaults. Other options can come through an extensibility mechanism.
Consider limiting the number of inputs to only those necessary for the component's default behavior.
One trick to expand your component configuration options without adding multiple inputs is to add one more that receives all extended options as JSON. Make sure you explain the available JSON options on both the input and component descriptions.
If you have a lot of advanced scenarios, consider having a page dedicated to explaining the extensible options. In case you're publishing your component to Forge, you include the explanation on the page that the Try Now button links to.
Style classes to simplify customization
Be sure to take advantage of expressions in the Style Classes property. This approach generates code that changes the "class" attribute instead of using the "style" one, preventing unnecessary data on your DOM and simplifying customization. Keep in mind that everything that's static but conditional can be a CSS class, so use the inline style attribute only when you have a dynamic style. You may also leave a comment explaining the math behind it.
CSS selectors for enhanced reusability
Using only classes in your CSS selectors limits reusability of your component because you can only reference one instance of it, limiting multiple uses on the same screen. Follow the steps below to create CSS selectors in relation to a specific instance of the component, instead of using classes only:
Add a wrapper div and set the "Name" property.
Build your selector with a CSS class related to the ID you passed as an input. In this example, the element where the CSS class exists is a child of the Wrapper element, but it could also be the same element.
Balancing limits and customization
Avoid the "!important" CSS tag. Though it could sometimes be the right choice, it does override a developer's custom CSS, limiting customization.
Avoid setting colors and buttons, as they aren't east to override. If you have UI elements that trigger actions, such as buttons, consider replacing them with placeholders so developers can add their own UI elements and decide what actions or logic to execute.
Creating for extensibility
Using lifeCycle events correctly (Mobile and Reactive Web Apps only)
When developing a component, understanding the lifecycle of screens and blocks helps you to control the component's data and behavior. For example, this understanding helps you when defining default values for variables or deleting data that becomes irrelevant. Having a complete undersatnding of the lifecycle behavior allows you to predict issues and optimize performance.
See the documentation for Screen and Block Lifecycle Events for more information. This section covers the most relevant events for components.
Use OnInitialize to define default values for variables
Imagine that you have a screen with a local boolean variable with a default
False value. After the screen logic executes, the value changes to
True, and you navigate to another screen. At runtime, a variable's value (defined as model) is saved, so you can restore it when a Back Navigation occurs (also known as Previous Screen Navigation).
After this navigation, the variable is
True, but you might want to reset the screen state. So when you're using variables with default values, always consider these navigation scenarios, and, if needed, assign the default value on the OnInitialize event handler.
OnDestroy to prevent memory leaks
Some components create global variables, event listeners, or other data during their usage. Consider removing/deleting that data on the OnDestroy event.
OnRender for rendering and data changes
If your component includes UI, the OnRender event is important because it runs each time the Screen or Block is rendered. For example, it runs whenever data changes on a screen.
Configuring a good preview
For a better development experience, ensure a great preview displays in Service Studio. A good preview helps developers avoid undesirable UI element styles.
False conditions for better previews
When you use an If widget with a
False condition, developers see a friendly UI that allows them to hide placeholders that occupy a large amount of UI space, for example, a side panel or bar.
The following example of the Silk Ui Mobile FloatingActions pattern provides a clickable circle that expands to provide extra options:
Service Studio CSS Tags for a better development experience
CSS properties prefaced with -servicestudio- can improve the preview and development experience. This approach allows you to more easily adapt the layout during development time, for example, when you have a placeholder with an "absolute" that you want to keep static to allow the developer to add content. Because the editor renders all CSS instructions, the elements might become inaccessible in the editor.
Using -servicestudio- tags helps developers set the values without compromising other features:
Sample content inside placeholders
A placeholder should have sample content if it's directly connected to a behavior or structure. This allows the developer to know what the component expects in that placeholder. For example, both Silk UI Search and AnimatedLabel blocks have an input as sample because the blocks require one to work properly.
Avoid using sample content for simple things, such as texts and images, because it's distracting and developers must delete it. Instead, add sample content only if it's mandatory or it helps developers.
Build for the future by making sure your component can support multiple blocks on the same screen.
Consider concurrency scenarios. OutSystems already helps with this in the background by keeping blocks separated, but you still need to be aware of actions triggered by users. For example, several fast clicks on a button, triggering multiple calls to the server, is one possible concurrency scenario.
If the component fetches data, consider allowing the developer to fetch it outside the component and provide that data (to the component) as an input parameter, instead of having the component fetch the data.
When using external resources (for example, external libraries, or JSON files), consider reusing them instead of fetching them again. For example, the System Action RequireScript uses the cached file if once you've requested it.
Read the complete description on how OutSystems handles scalability, in a way that's adjustable to specific requirements.
When it comes to security, consider that any device is vulnerable. Therefore, focus on keeping data safe.
Since everything on the client side can be accessed and is more vulnerable, make sure you protect any sensitive data. Use this plugin to cipher your data.
Keep server-side communications safe by implementing server-side validations, especially permission roles.
When possible, enforce HTTPS security on your screens, pages, and integrations.
Read the complete description of how OutSystems handles security, including its default features that work to ensure data security during the entire lifecycle of mobile and web apps.
OutSystems prevents performance bottlenecks with design-time validation. When OutSystems generates the source code for applications, it optimizes code performance at all application layers.
You should still test your component with more than a few sample records. Set up a scenario that's as realistic as possible, and test your component under probable conditions.
Go the extra mile and consider peaking the number of records or conditions, or test scenarios with thousands of concurrent users. If your component reaches the server, it could impact performance.
Creating a component: Presenting it
Reusability and simplicity are key when creating a component. Components should be easy to understand and should focus on the main use cases. Names, descriptions and visual cues, such as icons, make it easier for developers to understand and use your component.
Names and descriptions
Names and descriptions help people to understand the goal of your component and how to use it. Names should be clear and should clarify what a component does. The public-facing parts of the component should have comprehensive, front-loaded and succinct descriptions.
A complete component overview also describes things such as library URLs, API versions, and references to external resources.
Keep in mind these naming conventions:
- Use meaningful names (for example, "Cropper", instead of "Cppr").
- Use PascalCase (for example, "FirebaseReceiver", instead of "Firebasereceiver").
- Use event names that start with "On" (for example, "OnReady", instead of "Ready").
Take a look at the complete list of OutSystems Naming Conventions.
Descriptions provide visual cues during development, enhancing that experience. For example:
Hover over application dependencies to see their description.
Check a module's description, by hovering in the Manage Dependencies popup window.
The search box allows you to search for the name and the description. Note that only components with an icon appear in the toolbox on the left.
Icons for visual reference
Visual programming environments have graphical or iconic elements to for interactive use. Icons are important because developers actually see them in the development environment.
The first recommendation is to use images that identify the component. For example, the Firebase Connector has as an icon, which is the Firebase company logo.
The second recommendation is to use the same image from the Application level all the way to the Actions level. This approach provides consistency and allows developers to easily identify all your component pieces.
Setting an application icon defines the main icon of your component. Users can relate everything that has this icon to the component. The application icon appears throughout the component and its references. For example, it could even appear in the component's documentation.
For application icons, use .png format to allow transparencies and 1024 x 1024 px as the size. Keep in mind the file size as large files slow the screen-loading speed.
Setting icons for modules allows developers to relate them to your application in the Manage Dependencies window.
Module icons should be 1024 x 1024 px.
Setting icons for blocks helps developers identify which component the UI element belongs to. When you search for elements, you can immediately see the blocks related to your component.
The size of block icons (.ico format) should be 32 x 32 px.
Setting image icons for actions helps to easily identify which component an action in a flow belongs to.
Client and Server Action icons
Service Studio visually distinguishes Client Actions and Server Actions. This visual dinstinction helps users understand not only the context of the action but also the relevant scope.
For example, if you're working on a client-side form for an offline application you use client-side validations and avoid Server Actions. Your component should make the same distinction if possible and applicable.
If you're implementing both Client and Server Actions, consider using different styles for the same icon. OutSystems already provides you with the default icons for Client and Server Actions. If you want to change them, make replacment icons 32 x 32 px.
Maintaining a component
You should keep your code tidy and organized. This makes it easier when you come back to it months later; it also makes it easier for others to use it. When you publish on Forge, make it easier for different developeres to unerstand and use your code.
Comment changes to code between versions, so people can understand what changed and why.
Notes and comments
If you have logic-related code in your component that's unclear, then add notes or comments explaining its purpose. Comemnts help other developers follow and improve the code. Even when code isn't complex, add simple labels, to it understandable.
Sharing a component
If you think your component can help other developers, consider sharing it in the Forge.
When sharing components in Forge, take the following into account: