Skip to content
Back to skills

Source 5c107a9135cd132d34cdbd48

ASecurity

Fix Control renderer issues that UI5 linter reports but cannot auto-fix. Use this skill when linter outputs these rules: - `no-deprecated-control-renderer-declaration` - For missing renderer declaration, string-based renderer declaration, implicit renderer auto-discovery (removed in modern UI5) - `no-deprecated-api` - For missing apiVersion:2 in renderer objects, missing IconPool import when using oRm.icon(), deprecated rerender() override. NOTE: For Library.init() apiVersion errors, use fix-...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
developmentjavascriptjavabashapiperformance

Works with

  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add gutsofpratik/OpenClaude-Skills --skill source-5c107a9135cd132d34cdbd48 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Source 5c107a9135cd132d34cdbd48?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Source 5c107a9135cd132d34cdbd48
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gutsofpratik-source-5c107a9135cd132d34cdbd48/badge)](https://www.skillsdirectory.com/skills/gutsofpratik-source-5c107a9135cd132d34cdbd48)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: fix-control-renderer
description: |
  Fix Control renderer issues that UI5 linter reports but cannot auto-fix. Use this skill when linter outputs these rules:
  - `no-deprecated-control-renderer-declaration` - For missing renderer declaration, string-based renderer declaration, implicit renderer auto-discovery (removed in modern UI5)
  - `no-deprecated-api` - For missing apiVersion:2 in renderer objects, missing IconPool import when using oRm.icon(), deprecated rerender() override. NOTE: For Library.init() apiVersion errors, use fix-library-init instead.
  - `ui5-class-declaration` - For non-static renderer property in ES6 classes
  Trigger on: "missing a renderer declaration", "Deprecated declaration of renderer", "deprecated renderer", "apiVersion" (in renderer context), "IconPool", "rerender", "renderer must be a static property"
  Converts legacy renderer patterns to modern apiVersion: 2 format with proper module imports.
---

# Fix Control Renderer Issues

This skill fixes Control renderer issues that the UI5 linter detects but cannot auto-fix because they require understanding of the control's rendering behavior and module dependencies.

## Linter Rules Handled

| Rule ID | Message Pattern | This Skill's Action |
|---------|-----------------|---------------------|
| `no-deprecated-control-renderer-declaration` | Control '...' is missing a renderer declaration | Add `renderer: null` or import renderer |
| `no-deprecated-control-renderer-declaration` | Deprecated declaration of renderer '...' for control '...' | Import renderer module and assign directly |
| `no-deprecated-api` | Use of deprecated renderer detected. Define explicitly the {apiVersion: 2} | Add `apiVersion: 2` to renderer object |
| `no-deprecated-api` | "sap/ui/core/IconPool" module must be imported when using RenderManager's icon() method | Add IconPool import |
| `no-deprecated-api` | Override of deprecated method 'rerender' in control '...' | Remove override, move code to lifecycle hooks |
| `ui5-class-declaration` | The control renderer must be a static property | Make renderer property static |

## When to Use

Apply this skill when you see linter output like:
```
MyControl.js:5:1 error Control 'my.app.control.MyControl' is missing a renderer declaration  no-deprecated-control-renderer-declaration
MyControl.js:10:5 error Deprecated declaration of renderer 'my.app.control.MyControlRenderer' for control 'my.app.control.MyControl'  no-deprecated-control-renderer-declaration
MyControl.js:15:5 error Use of deprecated renderer detected. Define explicitly the {apiVersion: 2} parameter  no-deprecated-api
MyControl.js:20:5 error "sap/ui/core/IconPool" module must be imported when using RenderManager's icon() method  no-deprecated-api
MyControl.js:25:5 warning The control renderer of 'MyControl' must be a static property  ui5-class-declaration
```

## Background: Why apiVersion: 2?

UI5's rendering framework evolved from an immediate DOM manipulation model (apiVersion 1) to a semantic rendering model (apiVersion 2 and 4). The key differences:

- **apiVersion 1 (deprecated)**: Direct DOM manipulation via `oRm.write()`, `oRm.writeAttribute()`, etc.
- **apiVersion 2**: Semantic methods like `oRm.openStart()`, `oRm.openEnd()`, `oRm.text()`, `oRm.close()`
- **apiVersion 4**: Same as 2, with additional performance optimizations (for modern UI5)

Without explicit `apiVersion`, UI5 assumes legacy rendering which causes synchronous loading and performance issues.

### Implicit Renderer Auto-Discovery (Removed in modern UI5)

In UI5 1.x, if a control doesn't declare a `renderer` property, the framework automatically tries to load a renderer module by appending `Renderer` to the control's module path. For example, for `sap/m/Button`, it checks whether `sap/m/ButtonRenderer` exists — if so, that module is loaded and used as the renderer.

This implicit auto-discovery is **removed in modern UI5**. Every control must explicitly declare its renderer. If a control relied on auto-discovery and has no `renderer` property, it will break at runtime. The linter flags this as `no-deprecated-control-renderer-declaration` with the message "Control '...' is missing a renderer declaration".

The modernization workflow is: check whether a `<ControlName>Renderer` module exists at the default path → if yes, import it via `sap.ui.define` → assign it to the `renderer` property.

## Fix Strategy

### 1. Missing Renderer Declaration

**Problem**: Control class doesn't declare a renderer at all.

```javascript
// Before - triggers no-deprecated-control-renderer-declaration
sap.ui.define([
    "sap/ui/core/Control"
], function(Control) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        metadata: {
            properties: { ... }
        }
        // No renderer declaration!
    });
});
```

**Fix Strategy A - No rendering needed** (control is abstract or uses child controls):
```javascript
// After - explicitly declare no renderer
sap.ui.define([
    "sap/ui/core/Control"
], function(Control) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        metadata: {
            properties: { ... }
        },

        renderer: null
    });
});
```

**Fix Strategy B - Renderer exists in separate file** (including implicit auto-discovery):

In UI5 1.x, many controls rely on the framework's implicit auto-discovery — they have no `renderer` property, but a `<ControlName>Renderer.js` file exists at the default path and gets loaded automatically. Since this auto-discovery is removed in modern UI5, you need to make the import explicit.

**How to find the renderer:**
1. Derive the expected renderer path: take the control's module path and append `Renderer`. For `my/app/control/MyControl`, check for `my/app/control/MyControlRenderer`.
2. Look for the file in the project (e.g., `MyControlRenderer.js` in the same directory as the control).
3. If the renderer file exists, import it and assign it. If it doesn't exist, use Fix Strategy A (`renderer: null`) or Fix Strategy C (inline renderer).

```javascript
// After - import and assign the renderer module
sap.ui.define([
    "sap/ui/core/Control",
    "./MyControlRenderer"
], function(Control, MyControlRenderer) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        metadata: {
            properties: { ... }
        },

        renderer: MyControlRenderer
    });
});
```

**Important**: After importing the renderer, also check whether the renderer module itself has `apiVersion: 2`. If not, that's a separate linter finding — see section 3 "Missing apiVersion in Renderer" below.

**Fix Strategy C - Add inline renderer**:
```javascript
// After - define renderer inline with apiVersion: 2
sap.ui.define([
    "sap/ui/core/Control"
], function(Control) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        metadata: {
            properties: { ... }
        },

        renderer: {
            apiVersion: 2,
            render: function(oRm, oControl) {
                oRm.openStart("div", oControl);
                oRm.class("myControl");
                oRm.openEnd();
                // Render content here
                oRm.close("div");
            }
        }
    });
});
```

### 2. String-Based Renderer Declaration

**Problem**: Renderer declared as string causes synchronous loading.

```javascript
// Before - triggers no-deprecated-control-renderer-declaration
sap.ui.define([
    "sap/ui/core/Control"
], function(Control) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        metadata: { ... },

        renderer: "my.app.control.MyControlRenderer"  // String = sync loading!
    });
});
```

**Fix Strategy**: Import the renderer module and assign directly.

```javascript
// After - import renderer module
sap.ui.define([
    "sap/ui/core/Control",
    "my/app/control/MyControlRenderer"
], function(Control, MyControlRenderer) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        metadata: { ... },

        renderer: MyControlRenderer
    });
});
```

### 3. Missing apiVersion in Renderer

**Problem**: Renderer object or function without apiVersion declaration.

```javascript
// Before - triggers no-deprecated-api
renderer: {
    render: function(oRm, oControl) {
        oRm.write("<div");
        oRm.writeControlData(oControl);
        oRm.write(">");
        oRm.write("</div>");
    }
}

// OR - function without apiVersion
renderer: function(oRm, oControl) {
    oRm.write("<div>");
    oRm.write("</div>");
}
```

**Fix Strategy**: Add `apiVersion: 2` and convert to semantic rendering API.

```javascript
// After - with apiVersion: 2 and semantic methods
renderer: {
    apiVersion: 2,
    render: function(oRm, oControl) {
        oRm.openStart("div", oControl);
        oRm.openEnd();
        oRm.close("div");
    }
}
```

**apiVersion 1 to apiVersion 2 Method Conversions:**

| Old Method (apiVersion 1) | New Method (apiVersion 2) |
|---------------------------|---------------------------|
| `oRm.write("<tag")` | `oRm.openStart("tag")` or `oRm.voidStart("tag")` |
| `oRm.write(">")` | `oRm.openEnd()` or `oRm.voidEnd()` |
| `oRm.write("</tag>")` | `oRm.close("tag")` |
| `oRm.write(text)` | `oRm.text(text)` |
| `oRm.writeControlData(oCtrl)` | Pass control as 2nd arg: `oRm.openStart("div", oControl)` |
| `oRm.addClass("cls")` | `oRm.class("cls")` |
| `oRm.writeAttribute("name", val)` | `oRm.attr("name", val)` |

For the complete conversion table with examples, read `references/renderer-api-mapping.md`.

### 4. Missing IconPool Import

**Problem**: Using `oRm.icon()` without importing IconPool.

```javascript
// Before - triggers no-deprecated-api
sap.ui.define([
    "sap/ui/core/Control"
], function(Control) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        renderer: {
            apiVersion: 2,
            render: function(oRm, oControl) {
                oRm.openStart("div", oControl);
                oRm.openEnd();
                oRm.icon("sap-icon://accept");  // IconPool not imported!
                oRm.close("div");
            }
        }
    });
});
```

**Fix Strategy**: Add IconPool to the imports. The import is required even though it's not directly referenced in code.

```javascript
// After - IconPool imported
sap.ui.define([
    "sap/ui/core/Control",
    "sap/ui/core/IconPool"  // Required for oRm.icon()
], function(Control, IconPool) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        renderer: {
            apiVersion: 2,
            render: function(oRm, oControl) {
                oRm.openStart("div", oControl);
                oRm.openEnd();
                oRm.icon("sap-icon://accept");
                oRm.close("div");
            }
        }
    });
});
```

### 5. Deprecated rerender() Override

**Problem**: Overriding `rerender()` method no longer works in UI5 1.121+.

```javascript
// Before - triggers no-deprecated-api
sap.ui.define([
    "sap/ui/core/Control"
], function(Control) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        renderer: { ... },

        rerender: function() {
            // Custom logic before rerendering
            this._prepareForRender();
            Control.prototype.rerender.apply(this, arguments);
            // Custom logic after rerendering
            this._finishRender();
        }
    });
});
```

**Fix Strategy**: Move pre-render logic to `onBeforeRendering()` and post-render logic to `onAfterRendering()`.

```javascript
// After - use lifecycle hooks instead
sap.ui.define([
    "sap/ui/core/Control"
], function(Control) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        renderer: { ... },

        onBeforeRendering: function() {
            // Called before each render (initial + re-renders)
            this._prepareForRender();
        },

        onAfterRendering: function() {
            // Called after each render (initial + re-renders)
            this._finishRender();
        }
    });
});
```

## Implementation Steps

1. **Run linter with --details** to get additional context:
   ```bash
   npx @ui5/linter --details
   ```

2. **Identify the error pattern** from linter output (rule ID + message)

3. **Determine the control's rendering needs**:
   - Does the control need custom rendering?
   - Is there an existing separate renderer file? Check the default path: `<ControlName>Renderer.js` in the same directory (UI5 1.x auto-discovery path)
   - Does the renderer use `oRm.icon()`?

4. **Apply the appropriate transformation**:
   - For missing declaration: Check if `<ControlName>Renderer.js` exists at the default path (auto-discovery). If yes, import and assign it. If no, add `renderer: null` or create an inline renderer
   - For string declaration: Convert to module import
   - For missing apiVersion: Add `apiVersion: 2` and convert render methods
   - For IconPool: Add the import to sap.ui.define dependencies
   - For rerender override: Move logic to lifecycle hooks
   - For non-static: Add `static` keyword (ES6 classes)

5. **Verify the fix** by re-running the linter

## Example Fix Session

Given linter output:
```
npx @ui5/linter --details

MyControl.js:5:1 error Deprecated declaration of renderer 'my.app.control.MyControlRenderer' for control 'my.app.control.MyControl'  no-deprecated-control-renderer-declaration
  Details: Defining the control renderer by its name may lead to synchronous loading of the control renderer module.
MyControl.js:20:5 error Use of deprecated renderer detected. Define explicitly the {apiVersion: 2} parameter in the renderer object  no-deprecated-api
  Details: See: https://ui5.sap.com/#/topic/c9ab34570cc14ea5ab72a6d1a4a03e3f
```

**Before:**
```javascript
sap.ui.define([
    "sap/ui/core/Control"
], function(Control) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        metadata: {
            properties: {
                text: { type: "string", defaultValue: "" }
            }
        },

        renderer: "my.app.control.MyControlRenderer"
    });
});

// MyControlRenderer.js (separate file)
sap.ui.define([], function() {
    "use strict";

    var MyControlRenderer = {};

    MyControlRenderer.render = function(oRm, oControl) {
        oRm.write("<div");
        oRm.writeControlData(oControl);
        oRm.addClass("myControl");
        oRm.writeClasses();
        oRm.write(">");
        oRm.writeEscaped(oControl.getText());
        oRm.write("</div>");
    };

    return MyControlRenderer;
});
```

**After:**
```javascript
sap.ui.define([
    "sap/ui/core/Control",
    "./MyControlRenderer"
], function(Control, MyControlRenderer) {
    "use strict";

    return Control.extend("my.app.control.MyControl", {
        metadata: {
            properties: {
                text: { type: "string", defaultValue: "" }
            }
        },

        renderer: MyControlRenderer
    });
});

// MyControlRenderer.js (separate file) - updated
sap.ui.define([], function() {
    "use strict";

    var MyControlRenderer = {
        apiVersion: 2
    };

    MyControlRenderer.render = function(oRm, oControl) {
        oRm.openStart("div", oControl);
        oRm.class("myControl");
        oRm.openEnd();
        oRm.text(oControl.getText());
        oRm.close("div");
    };

    return MyControlRenderer;
});
```

## Notes

- Controls that extend these base classes do NOT need a renderer declaration:
  - `sap/ui/core/mvc/View`
  - `sap/ui/core/XMLComposite`
  - `sap/ui/core/webc/WebComponent`
  - `sap/uxap/BlockBase`

- `apiVersion: 4` is also valid and provides additional optimizations for modern UI5

- When converting from apiVersion 1 to 2, ensure all `write()` calls are properly converted to semantic methods

- The IconPool import is needed at module load time for icon font registration, even if `IconPool` variable is not used in code

## Related Skills

- **fix-js-globals**: For `no-globals` errors in non-renderer JavaScript files (e.g., controllers, utilities), use fix-js-globals — it handles `sap.ui.define` dependency additions and global access replacement
- **fix-pseudo-modules**: If renderer code also has enum or DataType pseudo module imports, use fix-pseudo-modules for those specific issues
- **fix-library-init**: For `Library.init()` / `Lib.init()` apiVersion errors ("Deprecated call to ... Use the {apiVersion: 2} parameter"), use fix-library-init — it handles library initialization, not renderer objects

Files in this skill

  • PROVENANCE.json240 B
  • SKILL.md16.3 KB
  • UPSTREAM-LICENSE.txt11.1 KB
  • references/renderer-api-mapping.md3.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.