Skip to content

Fiori Elements or Freestyle SAPUI5? Choosing the Right Approach

The wrong choice is expensive in both directions: either you hand-write every screen and carry its maintenance for years, or you fight the template and pile up extensions. Decision criteria, the middle path and real code examples.

Mustafa Önder Mustafa Önder  ·  2 October 2026  ·  12 min read

Contents

  1. Why This Choice Matters So Much
  2. What the Two Approaches Really Are
  3. Fiori Elements in Practice: the Backend Draws the Screen
  4. Freestyle in Practice: You Write Every Behaviour
  5. Side-by-Side Comparison
  6. Before Going Freestyle: Extension Points
  7. The Middle Path: Flexible Programming Model
  8. Questions to Ask When Deciding
  9. Common Mistakes
  10. 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.

The real difference: In Elements the description of the screen lives in the backend, in CDS annotations; in freestyle it lives in frontend code. That single difference affects everything from who can maintain the app to how you test it.

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.

The key takeaway for ABAP teams: maintaining this application does not require a UI5 developer. A new column, a new filter or a new action is a CDS file that an ABAP developer changes in ADT. With Preview on the service binding you can see the result without even creating a frontend application.

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

Comparison of Fiori Elements and Freestyle SAPUI5 in terms of development, maintenance and typical use
CriterionFiori ElementsFreestyle SAPUI5
Speed on a standard list-detail screenVery high; the screen is generated from annotationsLow; every component is built by hand
FlexibilityWithin the limits of the template and its extension pointsUnlimited; the limit is the team's capacity
Design consistencyAutomatic; all apps behave the same wayDepends on the team's discipline
UI5 upgradesNew features arrive on their ownCleaning up deprecated APIs is your job
Who maintains it?A team that knows ABAP/CDSA team that knows UI5/JavaScript
Backend expectationAn annotated, well-modelled OData service (ideally RAP)Any OData or REST source
TestingBusiness rules in the backend, tested with ABAP UnitFrontend tests (QUnit, OPA5) needed in addition
Typical useList-detail, approval flows, master data maintenance, analytical listsScanning, 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 golden rule of extensions: use only documented extension points and public APIs. Code that reaches into the template's internal controls to change the DOM, or overrides private methods, breaks silently at the first UI5 upgrade. Such interventions, made through controller extensions in older OData V2-based Elements apps, are the typical reason apps break during upgrades.

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:

  1. Does the screen revolve around a business object? List, detail, create/change, approve/reject — if yes, the default choice is Fiori Elements.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
In short: Elements by default. Where the template falls short, extension points first, then FPM. Freestyle when the interaction itself is outside the template.

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
← Back to Blog

Contact

Get in touch for your projects.