Contents
- Why This Choice Matters So Much
- What the Two Approaches Really Are
- Fiori Elements in Practice: the Backend Draws the Screen
- Freestyle in Practice: You Write Every Behaviour
- Side-by-Side Comparison
- Before Going Freestyle: Extension Points
- The Middle Path: Flexible Programming Model
- Questions to Ask When Deciding
- Common Mistakes
- Conclusion
Almost every Fiori project starts with the same question: "Should we build this with Fiori Elements, or write it freestyle?" The answer is usually driven by team habit rather than technical assessment. A team that knows UI5 writes everything freestyle; an ABAP-heavy team tries to squeeze everything into Elements.
In this article I explain what the two approaches really are, the third path between them that most projects overlook, and criteria you can use to decide — with code examples. For a general introduction to Fiori design principles, you may want to read the Fiori and UX article first.
Why This Choice Matters So Much
Most of a Fiori app's cost does not show up in the first release but in the years after it: new field requests, UI5 upgrades, new devices, team changes. The choice of approach decides whose shoulders that cost lands on, and in what form.
- Unnecessary freestyle: When you hand-write a standard list-detail screen, you write everything Elements gives you out of the box — filter bar, variant management, table personalisation, export to Excel, message handling — and you maintain it too.
- Forced Elements: When you try to squeeze an interaction that does not fit the template into Elements, extensions that rely on the framework's internals pile up. Those extensions are candidates to break at the first UI5 upgrade.
Both mistakes are expensive to reverse, because changing the approach later in practice means rewriting the application.
What the Two Approaches Really Are
Fiori Elements: the template generates the screen
In Fiori Elements you do not write the screen; SAP's ready-made page templates (floorplans) generate it at runtime. The template reads the OData service's metadata and its annotations, and decides from them which field appears in the list, which in the filter bar, and which group it belongs to on the detail page.
Common floorplans: List Report + Object Page (list-detail), Worklist, Analytical List Page and Overview Page. The application itself is mostly a manifest.json and a few settings; there is little or no JavaScript.
Freestyle SAPUI5: you build the screen
In freestyle you write the XML views, controllers, model bindings, routing and error handling yourself. The whole SAPUI5 control library (sap.m, sap.ui.table, sap.f…) is at your disposal; the limit is how much code your team can write and maintain.
Freestyle does not mean "not Fiori". A freestyle app built with the right controls and following the design guidelines is fully Fiori compliant; the template just does not give you that compliance automatically — you earn it with effort.
Fiori Elements in Practice: the Backend Draws the Screen
Consider a sales order application modelled with RAP. Assume the CDS projection view is in place and marked with @Metadata.allowExtensions: true — I walked through building that layer step by step in the OData V4 / CDS article. The whole screen is described in a metadata extension file:
@Metadata.layer: #CUSTOMER
@UI.headerInfo: { typeName: 'Sales Order',
typeNamePlural: 'Sales Orders',
title: { type: #STANDARD, value: 'SalesOrder' } }
annotate entity ZC_SalesOrder with
{
@UI.facet: [ { id: 'General', purpose: #STANDARD, position: 10,
type: #IDENTIFICATION_REFERENCE, label: 'General Information' },
{ id: 'Items', purpose: #STANDARD, position: 20,
type: #LINEITEM_REFERENCE, label: 'Items',
targetElement: '_Item' } ]
@UI.lineItem: [ { position: 10 },
{ type: #FOR_ACTION, dataAction: 'approve', label: 'Approve' } ]
@UI.selectionField: [ { position: 10 } ]
@UI.identification: [ { position: 10 } ]
SalesOrder;
@UI.lineItem: [ { position: 20 } ]
@UI.selectionField: [ { position: 20 } ]
@UI.identification: [ { position: 20 } ]
Customer;
@UI.lineItem: [ { position: 30, criticality: 'StatusCriticality' } ]
@UI.identification: [ { position: 30, criticality: 'StatusCriticality' } ]
OverallStatus;
@UI.hidden: true
StatusCriticality;
}This single file produces: two fields in the filter bar, three columns with status colouring in the result table, an Approve button that becomes active when a row is selected, and two sections on the detail page — a general information form and an items table.
The business rule behind the Approve button lives in the backend as well. It is exposed with a single line in the projection behaviour definition:
projection;
strict ( 2 );
define behavior for ZC_SalesOrder alias SalesOrder
{
use update;
use action approve;
}When the button is enabled (feature control), which validations run, which field an error message is attached to — all of this is defined in the RAP behaviour layer, and Elements reflects it on its own. If the business object is draft-enabled, preserving unsaved changes is part of the package too.
There are also things you get without writing a single annotation. The List Report floorplan brings variant management, table personalisation, export to spreadsheet, message handling and keyboard accessibility out of the box. None of this is your code — so when UI5 is upgraded, the improvements arrive on their own.
Freestyle in Practice: You Write Every Behaviour
Now a different screen: a picking app used with handheld terminals in a warehouse. The user scans a barcode; the app finds the matching item, marks it, clears the input field and waits for the next scan. This screen is not a list of a business object but an interaction flow — and that is exactly where freestyle is strong.
<mvc:View controllerName="z.picking.controller.Main"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<Page title="{i18n>pickingTitle}">
<Input id="scanInput"
placeholder="{i18n>scanPlaceholder}"
submit=".onScan"/>
<List id="itemList"
mode="MultiSelect"
items="{/PickingItem}">
<ObjectListItem title="{Material}"
number="{Quantity}"
numberUnit="{Unit}"/>
</List>
</Page>
</mvc:View>sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageToast"
], function (Controller, MessageToast) {
"use strict";
return Controller.extend("z.picking.controller.Main", {
// The scanner types the barcode and sends Enter: the submit event fires
onScan: function (oEvent) {
const sBarcode = oEvent.getParameter("value").trim();
const oItem = this.byId("itemList").getItems().find(function (oListItem) {
return oListItem.getBindingContext().getProperty("Barcode") === sBarcode;
});
if (!oItem) {
MessageToast.show(this._text("barcodeNotFound", [sBarcode]));
return;
}
oItem.setSelected(true);
oEvent.getSource().setValue("");
},
_text: function (sKey, aArgs) {
return this.getView().getModel("i18n").getResourceBundle().getText(sKey, aArgs);
}
});
});Most barcode scanners on handheld terminals behave like a keyboard: they type the scanned value into the input field and send Enter. That is why the submit event is enough; no extra hardware library is needed.
The code is short — but notice what is missing: filtering, sorting, variants, export to Excel, paging, collected message display. If you need any of them, you add each one yourself. The price of freestyle is not paid in the first release, but with every new item on that list.
Side-by-Side Comparison
| Criterion | Fiori Elements | Freestyle SAPUI5 |
|---|---|---|
| Speed on a standard list-detail screen | Very high; the screen is generated from annotations | Low; every component is built by hand |
| Flexibility | Within the limits of the template and its extension points | Unlimited; the limit is the team's capacity |
| Design consistency | Automatic; all apps behave the same way | Depends on the team's discipline |
| UI5 upgrades | New features arrive on their own | Cleaning up deprecated APIs is your job |
| Who maintains it? | A team that knows ABAP/CDS | A team that knows UI5/JavaScript |
| Backend expectation | An annotated, well-modelled OData service (ideally RAP) | Any OData or REST source |
| Testing | Business rules in the backend, tested with ABAP Unit | Frontend tests (QUnit, OPA5) needed in addition |
| Typical use | List-detail, approval flows, master data maintenance, analytical lists | Scanning, drag and drop, planning boards, wizards, device integration |
Before Going Freestyle: Fiori Elements Extension Points
When I hear "Elements can't do this", my first question is: at which layer did you try? In OData V4-based Fiori Elements, the solution should be looked for in these layers, in this order:
1. Annotations
Most problems are solved here: field order, grouping, hiding, colouring, value helps, and side effects that refresh other fields when one changes. A good share of "Elements is too limited" complaints actually come from an under-modelled service.
2. manifest.json settings
Table type (responsive, grid, analytical), whether data loads as soon as the page opens, Flexible Column Layout and default sorting are configured without writing code.
3. Extension points: custom columns, sections and actions
You place your own fragment and controller code at documented spots in the template. When these three layers are not enough, the next step is the Flexible Programming Model described in the next section.
For example, adding a custom map section right after the items on the detail page takes a few lines in the manifest:
"SalesOrderObjectPage": {
"type": "Component",
"id": "SalesOrderObjectPage",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"contextPath": "/SalesOrder",
"content": {
"body": {
"sections": {
"deliveryMap": {
"template": "z.orders.ext.fragment.DeliveryMap",
"title": "{i18n>deliveryMap}",
"position": { "placement": "After", "anchor": "Items" }
}
}
}
}
}
}
}The inside of that section is entirely yours: a map control, a custom chart or a third-party component. The rest of the page keeps being generated from annotations. Adding a custom button to the list table follows the same logic, defined in the manifest under controlConfiguration.
The Middle Path: Flexible Programming Model
Thinking of the choice as binary is no longer current. OData V4-based Fiori Elements offers an approach called the Flexible Programming Model (FPM): you use pieces of Elements — table, filter bar, form, chart — as building blocks in your own XML view. Those pieces are still driven by annotations; everything between them is your code.
<mvc:View controllerName="z.orders.ext.main.Main"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:macros="sap.fe.macros">
<Page title="{i18n>ordersTitle}">
<!-- Completely free: your own control, your own behaviour -->
<Input id="orderScanInput" submit=".onScan"/>
<!-- Elements building blocks: generated from CDS annotations -->
<macros:FilterBar id="orderFilter"
metaPath="@com.sap.vocabularies.UI.v1.SelectionFields"/>
<macros:Table id="orderTable"
metaPath="@com.sap.vocabularies.UI.v1.LineItem"
filterBar="orderFilter"/>
</Page>
</mvc:View>The controller of this page extends sap/fe/core/PageController instead of the standard Controller, so Elements' routing, draft and message infrastructure remains available. Such a page can also live as a custom page inside a regular List Report / Object Page application.
The practical meaning of FPM: "most of the screen is standard, part of it is special" no longer forces a choice between two bad options. The filter bar and table come ready with Elements' variant and personalisation capabilities; you only write the part that does not fit the template.
One constraint: FPM exists only in OData V4-based Elements (sap.fe). Existing V2-based apps do not have this path — one more reason to choose V4 for new development.
Questions to Ask When Deciding
For a new application request, working through these questions in order usually makes the decision on its own:
- Does the screen revolve around a business object? List, detail, create/change, approve/reject — if yes, the default choice is Fiori Elements.
- Can the backend be modelled with RAP/CDS? Elements cannot be better than the service behind it. If the service is a thin shell over an old BAPI, build the service properly first; RAP is also the foundation of the ABAP Cloud and clean core model. If the data genuinely cannot be modelled (for example, it is combined live from several systems), freestyle becomes a realistic option.
- How large is the part that does not fit the template? A section or a few buttons: extension points. A whole page: an FPM custom page. If the entire app is an interaction flow (scanning, a planning board, drag and drop): freestyle.
- Who will maintain it? If the company has no UI5 developer and will not have one, a freestyle app becomes orphaned at the first change request. This criterion should weigh as much as the technical ones.
- How long will the app live? A temporary tool used for a few months and a core process app that will live for years are not assessed the same way. For long-lived apps, the cost of UI5 upgrades is decisive.
Common Mistakes
Putting business rules in the frontend
Validations, calculations and authorisation checks belong in the RAP behaviour layer, not in a UI controller. This applies to both Elements and freestyle: a rule in the frontend can be bypassed, and other consumers of the same service — another app, an integration — never see it. A rule in the backend, on the other hand, can be tested with ABAP Unit.
Piling annotations into the app's local file
SAP Fiori tools lets you keep a local annotation file inside the application. That is reasonable for small settings specific to that one app; but field labels, value helps and groupings should live in CDS. Otherwise two apps using the same service show two different versions of the truth.
Saying "Elements is not flexible" without trying annotations
This is usually a skills gap, not a technical limit. Before deciding to go freestyle, test concretely whether the same need can be met with an annotation, a manifest setting or an extension point; the Page Map and Guided Development tools in SAP Fiori tools speed up that investigation.
Skipping the Fiori guidelines in freestyle
Custom colours, non-standard button placement, hand-written table controls — users feel the difference the moment they switch from this app to the others in the Launchpad. Freestyle's freedom is not a licence to give up design consistency.
Not assigning stable IDs in freestyle
Screen adaptations that key users make in the Launchpad (UI adaptation) and automated tests both rely on stable control IDs. Elements generates these IDs itself; in freestyle, giving every important control a meaningful ID is your responsibility.
Conclusion
The choice between Fiori Elements and freestyle may look like a matter of taste, but it is really an ownership decision: will the description of the screen live in the backend or in frontend code? A significant share of enterprise applications follow the list, detail and action pattern; for those, Elements is both faster to deliver and cheaper to live with over the years.
Freestyle is not unnecessary — it should just be the exception. When the interaction itself is outside the template, it is the right tool. The wide ground between the two is now covered by extension points and the Flexible Programming Model.
For your next Fiori request, make the first question not "which technology?" but "which business object does this screen revolve around?". You can find the scope I offer on Fiori projects on the Fiori & UI services page.
Get support on choosing the approach, designing RAP services and developing your Fiori applications.
Request an Initial Consultation