Skip to Content
Technical Articles

distribute and reuse UI5 custom controls via npm

tl;dr: the ui5-tooling supports using custom controls from the npm realm, for both development- and build-time. This makes re-use and distribution of custom controls easier than wrapping them in a library.

in here:

πŸ”₯ A barebones example repo is at


Until now, the primary means of reusing UI5 custom controls was to wrap them in a UI5 library and include that library in another UI5 app.
But next to application and (theme-)library, the ui5-tooling also allows for a type module. This describes a generic …well.. “module” that can be attached to a namespace (“resource root”, in UI5 speak).

# packages/
specVersion: "2.2"
type: module

      "/resources/cc/hello/world/": "./" # note the trailing slashes!

Above noted path configuration tells the UI5 runtime to look for runtime artefacts of the namespace in the current folder. (The resources part in the path is a requirement by the ui5-tooling.)

Subsequently, that namespace can be used by the UI5 runtime to include Controls, here: namespace, Control Greeter(.js).


dev-time: controls as npm modules

A UI5 custom control consists of the module logic and a renderer. They can either be split (e.g. look at sap.m.Text.js and sap.m.TextRenderer.js) or exist in the same file, using a static renderer function:

// packages/
sap.ui.define(["sap/ui/core/Control"], (Control) => {
    return Control.extend("", {
        renderer: {
            apiVersion: 2,
            render(oRM, oControl) {
                oRM.openStart("p", oControl)
                oRM.text("UI5 custom control: Hello World!")


A UI5 type module can be transferred into an npm module by simply giving it an npm init:

// file system view
β”œβ”€β”€ Greeter.js
β”œβ”€β”€ package.json
└── ui5.yaml

// package.json
  "name": "",
  "version": "0.0.1",
  "description": "UI5 custom (notepad) control demo",
  "license": "Beerware"

By combining the ui5-tooling descriptor ui5.yaml with a package.json, the UI5 custom control now is the regular npm module πŸ₯³ – and can be released and required as such!

hint: already note the naming convention

​ ui5-cc-$

for UI5 controls in npm-verse.

npm-based UI5 custom control in an app

In a UI5 app, it’s now as easy as requiring the custom control via dependencies and ui5.dependencies

// in packages/ui5-app/package.json
"dependencies": {
        "": "0.0.1"
"ui5": {
    "dependencies": [

…and using the namespace and control in a view:

<!-- packages/ui5-app/webapp/indexBareBones.html -->
<!-- in xml template literal -->
    < />            


There’s also no need to fiddle with data-sap-ui-resourceroots in the bootstrap html – the ui5-tooling automatically serves resources/**/* to the application.

A note on theming πŸ› : while UI5 libraries generally contain support for themes, the here presented npm-custom control approach can only include theme-specific optics by using CSS variables. Bespoken CSS variables are recognized by the ui5-tooling at development time! Please dig into the blog post by UI5 chieftain Peter Müßig for how to use CSS variables in your UI5 app – and thus, in UI5 custom controls.

3rd party modules in custom control

Custom controls might need 3rd party npm modules at runtime, most likely to re-use functionality already out there, and not having to reinvent the wheel(s).

Fortunately, shims from the ui5-tooling can be used for achieving just that: using npm modules with UI5 (see this nice blog post by Vivek C.). And the same concept can be applied to npm-based custom controls as well.

First, the 3rd party modules needs to be included into the custom control by standard npm mechanism npm install $dependency, resulting in a dependencies entry into package.json.

// from: ui5-cc-md
"dependencies": {
    "marked": "^1.2.9"

Then, use ui5.yaml‘s project-shim to patch the module through to the custom control at runtime:

# also from ui5-cc-md
specVersion: "2.2"
type: module
  name: ui5-cc-md

      "/resources/cc/md/": "./"
specVersion: "2.2"
kind: extension
type: project-shim
  name: marked
      specVersion: "2.2"
      type: module
        name: "marked"
            "/resources/cc/md/marked/": "./"

The above extension provides the 3rd party module marked from the custom control’s npm_modules folder in the same namespace as the custom control itself (, at the path /resources/cc/md/marked/.

The acutal marked module code can subsequently be loaded in the custom control via the standard sap.ui.define:

sap.ui.define(["./marked/marked.min"], (/* marked */) => { /*...*/ })

No more need to copy/paste 3rd party source code into the custom control realm!

addendum: build time for the app

When developing custom controls, there’s generally no need for build artefacts. Once the code runs as desired, the ui5 custom control can be published to npm or any other registry, and is ready to use. Optimizations -such as providing a minified, runtime-optimized version and a -dbg.js human readable counterpart, are possible, but not required.

From the viewpoint of the application the npm-distributed custom control is used in, a “standalone” build is necessary to make the app -including the custom control(s)- run in enviornments other than the ui5-tooling. By doing ui5 build --all , the npm-based custom controls are copied into the dist/resources folder (including all shims!) and are available for deployment.

$> ui5 build --all
info normalizer:translators:ui5Framework Using OpenUI5 version: 1.86.3
info builder:builder Building project ui5-app including dependencies...
info builder:builder πŸ›   (1/9) Building project
info builder:builder πŸ›   (2/9) Building project marked
info builder:builder πŸ›   (3/9) Building project ui5-cc-md
# ...

This results in a dist folder containing all custom controls and shims:

# sample app incl ui5-cc-md +
β”œβ”€β”€ cc
β”‚   β”œβ”€β”€ hello
β”‚   β”‚   └── world
β”‚   β”‚       β”œβ”€β”€ Greeter.js
β”‚   β”‚       β”œβ”€β”€ package.json
β”‚   β”‚       └── ui5.yaml
β”‚   └── md
β”‚       β”œβ”€β”€ Markdown.js
β”‚       β”œβ”€β”€ marked
# snip
β”‚       β”‚   β”œβ”€β”€ marked.min.js
β”‚       β”‚   β”œβ”€β”€ package.json
β”‚       β”‚   └── src
# snip
β”‚       β”‚       └── rules.js
β”‚       β”œβ”€β”€ package.json
β”‚       └── ui5.yaml
# ...

naming convention: ui5-cc-$

As with community-driven UI5 tasks (“ui5-task-$task”) and middlewares (“ui5-middleware-$middleware”), we propose to use the naming schema

​ ui5-cc-$

for custom controls on public npm registries.

With the prefix, it makes searching for custom UI5 controls easier. And by including the namespace in the npm module name, potential naming conflicts should be avoidable upfront.


  • ui5-cc-md β†’ only holds 1 custom control, Markdown, in namespace md
  • β†’Β uses namespace for included custom controls

πŸš€ Sooo, now, go go go and create and use awesome UI5 custom controls via npm!

You must be Logged on to comment or reply to a post.
  • someone recently asked, why in the code-sample from ui5-cc-md...

    sap.ui.define(["./marked/marked.min"], (/* marked */) => { /*...*/ })

    ... the markedΒ part is commented out: this is due how the referenced marked.minΒ is exporting marked at runtime, namely directly into the global scope. So no need to import the source as a dedicated var, as it is in window.markedanyway.

    hth, v.


  • Hi Volker,

    Thanks for the great blog. It's good to see a possibility to use npm packages for reuse scenarios in UI5. We tried it out for one of our libraries and it works perfectly in standalone mode. Alas, it does not work when the consuming app runs in Launchpad. The resources folder is not loaded when the app is initially loaded and we get an error the the library is not found. Did you face such issues when running in Launchpad as well?

    Best regards,