UI5 Table Best Practices
Apply these guidelines whenever generating, reviewing, or troubleshooting UI5 table code in freestyle applications.
UI5 version baseline: SAPUI5 1.136+ LTS. All features mentioned are available from this version unless noted.
When to load each reference
Load before producing any output. Do not work from memory.
Core Rules
Mandatory
- Choose the table type using the Selection Matrix before writing any code.
- Use the
rows aggregation for sap.ui.table.* and items for sap.m.Table. Never swap them.
- Use
sap.m.p13n.Engine for personalization. Never build custom personalization dialogs.
- Set
ariaLabelledBy on every table, referencing the table title control.
- Align cell content by data type: numbers and dates right (
hAlign="End"), text and links left.
- Use appropriate cell templates:
sap.m.Text for display, sap.m.ObjectNumber for numbers, sap.m.Link for navigation.
- Request
$count=true from the back end for sap.ui.table.* when a total count is required.
- Use the
rowMode aggregation (not the deprecated visibleRowCountMode property) for sap.ui.table.* (UI5 1.119+).
Prohibitions
- Do not use global variables. Use
sap.ui.define AMD modules or ES6 imports.
- Do not enable text wrapping in
sap.ui.table.* cells — it breaks virtualization.
- Do not assume
sap.ui.export.Spreadsheet is available. Detect the library before use.
- Do not use
sap.ui.table.Table for mobile-first scenarios. Use sap.m.Table.
- Do not use
sap.m.Table for datasets with 1000+ rows that require virtualization.
- Do not place multiple interactive elements in one
sap.ui.table.Table cell.
- Do not return enum objects from formatters. Return string literals or primitive values.
- Do not use formatters for
ColumnListItem highlight. Use direct data binding.
- Do not access models without checking availability — causes "Cannot read properties of undefined".
- Do not mix type namespaces: never use
sap.ui.model.odata.type.* with a JSON model, or sap.ui.model.type.* with OData.
OData V4 policy
Prefer SAP Fiori elements building blocks over freestyle tables for OData V4. Use sap.ui.mdc.Table only when Fiori elements is out of scope.
Selection Matrix
| Table type |
Use when |
Do not use when |
sap.m.Table |
Mobile/responsive, pop-in behavior, JSON models, fewer than 100 rows |
1000+ rows, virtualization required, desktop-only, cell selection needed |
sap.ui.table.Table |
Desktop, 1000+ rows, virtualization, fixed columns, dense data |
Mobile-first, pop-in required, text wrapping, small datasets |
sap.ui.table.TreeTable |
Hierarchical data, expand/collapse, parent-child relationships |
Flat data, mobile-first, grouping (not hierarchy) |
SmartTable |
OData V2, annotations, automatic columns, smart filtering |
JSON-only, precise control required, OData V4 |
sap.ui.mdc.Table |
OData V4 freestyle (when Fiori elements is ruled out), delegate pattern |
JSON-only, simple apps, OData V2 |
Dataset size guide
| Rows |
Recommended table |
Strategy |
| < 100 |
sap.m.Table |
Simple binding, growing optional |
| 100–1000 |
sap.ui.table.Table |
Virtualization, threshold=100 |
| 1000+ |
sap.ui.table.Table |
Virtualization, threshold=100–500, $count=true when needed |
Common Errors
| Symptom |
Cause |
Fix |
| No data displayed |
Incorrect binding path or missing model |
Verify bindRows/bindItems path and model attachment. |
Rows not scrolling (sap.ui.table.*) |
Count not requested |
Set $count=true for OData when a total count is required. |
Selection not working (sap.ui.table.*) |
Plugin conflict |
Do not call the table selection API when a selection plugin is applied; use the plugin API instead. |
| Text wrapping issues |
Wrapping enabled in sap.ui.table.* |
Use fixed-height content or switch to sap.m.Table. |
| Copy/paste not working |
Plugin not attached or wrong namespace |
Attach the correct plugin (see Drag & Drop section). |
| Personalization not persisting |
Engine not configured |
Verify sap.m.p13n.Engine registration. |
CopyProvider error |
extractData not defined |
Implement extractData on the plugin. |
| Table not visible |
Invalid container structure |
Use a valid container (see Container Structures). |
| OData types on JSON model |
Wrong type namespace |
Match the type namespace to the model: sap.ui.model.type.* for JSON, sap.ui.model.odata.type.* for OData V2, sap.ui.model.odata.v4.type.* for OData V4. |
| Excel export fails silently |
Library not loaded or invalid extractData |
Detect the library, return a 2D array from extractData, ensure dataSource binding. |
| "No Data Available" |
Model not set before binding |
Set the model in Component.init() before router initialization. |
Container Structures
Valid
| Structure |
Use case |
View > Page > content > Table |
Standard page |
View > Page > content > Panel > Table |
Grouped content |
View > Page > content > IconTabBar > items > IconTabFilter > Table |
Tabs |
View > SplitApp > detailPages > Page > Table |
Master-detail |
View > Dialog > content > Table |
Modal |
View > Table |
Standalone |
Invalid (and why)
| Structure |
Issue |
Page > content > VBox > Table |
VBox needs explicit height — table becomes invisible |
Page > VBox > Table |
VBox not in content — table not rendered |
Page > content > FlexBox > Table |
Sizing conflict — table collapses |
Page > content > ScrollContainer > Table |
Double scrolling — virtualization breaks |
Performance & Accessibility
Anti-patterns to avoid
- Handcrafted personalization dialogs (use
sap.m.p13n.Engine).
- Text wrapping in
sap.ui.table.Table cells.
- Multiple interactive elements in one GridTable cell.
- Mixing OData V2
SmartTable with V4 services.
- Deep nesting in cell templates.
- Fixed
threshold without load testing.
- Unconditional
$count=true (request count only when needed).
Accessibility checklist
- Set
ariaLabelledBy on every table, referencing the visible title.
- Use
sap.m.Text (not raw text nodes) as cell templates.
- For
sap.ui.table.Table: test keyboard navigation (Tab, arrow keys, Space/Enter for selection).
- Verify that the personalization dialog is keyboard-operable.
- Test with a screen reader (NVDA/JAWS on Windows, VoiceOver on macOS) before shipping.