Developing Custom UI5 Web Components

Note: All examples in this tutorial are taken from the Demo UI5 Web Component (ui5-demo), generated by the package initialization script. For more information on creating a new package with a demo Web Component inside, click here.

The class definition

The main file representing the Web Component is Demo.js.

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";

// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";

// Styles
import DemoCss from "./generated/themes/Demo.css.js";

import { PLEASE_WAIT } from "./generated/i18n/i18n-defaults.js";


const metadata = {
	tag: "ui5-demo",
	properties: {
	},
	slots: {
	},
	events: {
	},
};

class Demo extends UI5Element {
	static get metadata() {
		return metadata;
	}

	static get render() {
		return litRender;
	}

	static get template() {
		return DemoTemplate;
	}

	static get styles() {
		return DemoCss;
	}

	static async onDefine() {
		Demo.i18nBundle = await getI18nBundle("my-ui5-web-components");
	}

	get pleaseWaitText() {
		return Demo.i18nBundle.getText(PLEASE_WAIT);
	}
}

Demo.define();

export default Demo;

The UI5Element base class

Every UI5 Web Component must extend the base class UI5Element, provided by the @ui5/webcomponents-base package:

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";

class Demo extends UI5Element {

}

export default Demo;

The metadata object

Metadata is a JavaScript object, containing information about the public interface of a UI5 Web Component (tag name, properties, slots, events, etc.).

Metadata is passed via the metadata static getter:

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";

const metadata = {
	tag: "ui5-demo",
	properties: {
	},
	slots: {
	},
	events: {
	},
};

class Demo extends UI5Element {
	static get metadata() {
		return metadata;
	}
}

export default Demo;

So far, we’ve defined a ui5-demo Web Component with no properties, slots or events.

For a complete reference to all metadata entities, click here.

The render engine

UI5 Web Components are agnostic of the DOM render engine used. However, all standard UI5 Web Components (@ui5/webcomponents, @ui5/webcomponents-fiori, etc.) use lit-html as the rendering technology of choice.

The render engine is defined via the render static getter:

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";

const metadata = {
	tag: "ui5-demo",
	properties: {
	},
	slots: {
	},
	events: {
	},
};

class Demo extends UI5Element {
	static get metadata() {
		return metadata;
	}

	static get render() {
		return litRender;
	}
}

export default Demo;

Here we import LitRenderer.js from the @ui5/webcomponents-base package which is a very tiny wrapper around lit-html.

The template

Now that we’ve defined the rendering technology of choice, we can pass a template in that technology’s syntax.

This is done via the template static getter:

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";

// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";

const metadata = {
	tag: "ui5-demo",
	properties: {
	},
	slots: {
	},
	events: {
	},
};

class Demo extends UI5Element {
	static get metadata() {
		return metadata;
	}

	static get render() {
		return litRender;
	}

	static get template() {
		return DemoTemplate;
	}
}

export default Demo;

The standard UI5 Web Components use handlebars templates that are automatically converted to lit-html syntax by the build script.

If you have a Demo.hbs file along with the Demo.js file, the build script is going to create for you generated/templates/DemoTemplate.lit.js file. Therefore, we pass the content of this file to the template static getter.

For more information, see the next chapter of this tutorial.

The CSS definitions

You can pass CSS to be inserted in the Shadow Root of the UI5 Web Component by using the styles static getter:

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";

// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";

// Styles
import DemoCss from "./generated/themes/Demo.css.js";

const metadata = {
	tag: "ui5-demo",
	properties: {
	},
	slots: {
	},
	events: {
	},
};

class Demo extends UI5Element {
	static get metadata() {
		return metadata;
	}

	static get render() {
		return litRender;
	}

	static get template() {
		return DemoTemplate;
	}

	static get styles() {
		return DemoCss;
	}
}

export default Demo;

If you have a themes/Demo.css file, the build script will automatically generate for you a generated/themes/Demo.css.js file, which in addition to your component’s CSS, also contains definitions for all CSS Vars for the default theme. You can define your own CSS Vars for each theme in the respective theme directory in themes/:

File Descriptions
themes/sap_belize/parameters-bundle.css Values for the component-specific CSS Vars for the sap_belize theme.
themes/sap_belize_hcb/parameters-bundle.css Values for the component-specific CSS Vars for the sap_belize_hcb theme.
themes/sap_fiori_3/parameters-bundle.css Values for the component-specific CSS Vars for the sap_fiori_3 theme.
themes/sap_fiori_3_dark/parameters-bundle.css Values for the component-specific CSS Vars for the sap_fiori_3_dark theme.

For more information, see the CSS chapter of this tutorial.

Defining the Web Component

Defining a Web Component is necessary in order to register it in the browser.

This is done by calling the UI5Element.define static method:

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";

// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";

// Styles
import DemoCss from "./generated/themes/Demo.css.js";

const metadata = {
	tag: "ui5-demo",
	properties: {
	},
	slots: {
	},
	events: {
	},
};

class Demo extends UI5Element {
	static get metadata() {
		return metadata;
	}

	static get render() {
		return litRender;
	}

	static get template() {
		return DemoTemplate;
	}

	static get styles() {
		return DemoCss;
	}
}

Demo.define();

export default Demo;

Adding i18n support

There are 2 steps to do that:

  1. Get and assign an i18n bundle during component definition
     await Demo.i18nBundle = getI18nBundle("my-ui5-web-components");
    
  2. Get texts from the bundle, according to the currently configured language return Demo.i18nBundle.getText(PLEASE_WAIT);

The getI18nBundle method is provided by the i18nBundle.js module from the @ui5/webcomponents-base package.

So the final source code is:

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";

// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";

// Styles
import DemoCss from "./generated/themes/Demo.css.js";

import { PLEASE_WAIT } from "./generated/i18n/i18n-defaults.js";


const metadata = {
	tag: "ui5-demo",
	properties: {
	},
	slots: {
	},
	events: {
	},
};

class Demo extends UI5Element {
	static get metadata() {
		return metadata;
	}

	static get render() {
		return litRender;
	}

	static get template() {
		return DemoTemplate;
	}

	static get styles() {
		return DemoCss;
	}

	static async onDefine() {
		Demo.i18nBundle = await getI18nBundle("my-ui5-web-components");
	}

	get pleaseWaitText() {
		return Demo.i18nBundle.getText(PLEASE_WAIT);
	}
}

Demo.define();

export default Demo;

Please, note that here we use the onDefine method of UI5Element in order to ensure that i18n resources have been fetched before the Web Component has been defined. The used namespace for resource registration (in this example my-ui5-web-components) is the name property of your package.json file.

The template file

The template of the Web Component is in the Demo.hbs file. In this particular example it looks like this:

<div>This is: ui5-demo. </div>

The context in the template is the Web Component instance, therefore you can directly use any properties/getters on the object. Here, we see the pleaseWaitText getter, defined in the previous step.

As explained above, the .hbs file is transformed by the build script to a .js file in the lit-html syntax. More specifically, this file is provided to the Web Component class.

The CSS

Let’s inspect the following files (one with CSS declarations, the others with CSS Vars values for the themes).

File Purpose
themes/Demo.css All CSS rules for the Web Component, same for all themes; will be inserted in the shadow root.
themes/sap_belize/parameters-bundle.css Values for the component-specific CSS Vars for the sap_belize theme.
themes/sap_belize_hcb/parameters-bundle.css Values for the component-specific CSS Vars for the sap_belize_hcb theme.
themes/sap_fiori_3/parameters-bundle.css Values for the component-specific CSS Vars for the sap_fiori_3 theme.
themes/sap_fiori_3_dark/parameters-bundle.css Values for the component-specific CSS Vars for the sap_fiori_3_dark theme.

In the Demo.css file, in addition to other selectors, we have:

:host {
    border: 2px solid var(--ui5-demo-border-color);
    background-color: var(--sapBackgroundColor);
    color: var(--sapTextColor);
}

The CSS vars, starting with --sap are standard and provided by the framework. All the rest are custom for the specific Web Component.

Respectively, the definitions file for, let’s say sap_fiori_3, contains:

:root {
    --ui5-demo-border-color: green;
}

What’s important to understand here is that you author all the .css files listed in the table above, but the build script generates from them a single .js file for you, and this is namely the file you pass to the Web Component class: generated/themes/Demo.css.js.

Next: Understanding UI5 Web Components Metadata