diff --git a/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/AnalyticalTableFeatureExamples.stories.tsx b/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/AnalyticalTableFeatureExamples.stories.tsx
index 07b2ec088dc..b43b46b796b 100644
--- a/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/AnalyticalTableFeatureExamples.stories.tsx
+++ b/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/AnalyticalTableFeatureExamples.stories.tsx
@@ -1,6 +1,7 @@
import dataSmall from '@sb/mockData/Friends50.json';
import dataLarge from '@sb/mockData/Friends500.json';
import type { Meta, StoryObj } from '@storybook/react-vite';
+import NoDataIllustration from '@ui5/webcomponents-fiori/dist/illustrations/NoData.js';
import '@ui5/webcomponents-icons/dist/sort-ascending.js';
import '@ui5/webcomponents-icons/dist/sort-descending.js';
import '@ui5/webcomponents-icons/dist/reset.js';
@@ -18,6 +19,7 @@ import {
} from '../../../../enums/index.js';
import { Button } from '../../../../webComponents/Button/index.js';
import { Dialog } from '../../../../webComponents/Dialog/index.js';
+import { IllustratedMessage } from '../../../../webComponents/IllustratedMessage/index.js';
import { Input } from '../../../../webComponents/Input/index.js';
import { MultiComboBox } from '../../../../webComponents/MultiComboBox/index.js';
import { MultiComboBoxItem } from '../../../../webComponents/MultiComboBoxItem/index.js';
@@ -29,7 +31,7 @@ import { Text } from '../../../../webComponents/Text/index.js';
import { ToggleButton } from '../../../../webComponents/ToggleButton/index.js';
import { FlexBox } from '../../../FlexBox/index.js';
import { ObjectStatus } from '../../../ObjectStatus/index.js';
-import type { AnalyticalTableColumnDefinition } from '../../index.js';
+import type { AnalyticalTableColumnDefinition, AnalyticalTablePropTypes } from '../../index.js';
import { AnalyticalTable } from '../../index.js';
import meta from '../AnalyticalTable.stories.js';
@@ -456,9 +458,16 @@ export const LoadingStates: Story = {
const [loading, setLoading] = useState(false);
const [showOverlay, setShowOverlay] = useState(false);
const [alwaysShowBusyIndicator, setAlwaysShowBusyIndicator] = useState(false);
+ const [customNoData, setCustomNoData] = useState(false);
const data = useMemo(() => (hasData ? dataSmall : []), [hasData]);
+ const NoDataComponent: AnalyticalTablePropTypes['NoDataComponent'] = !customNoData
+ ? undefined
+ : loading
+ ? undefined
+ : (props) => ;
+
return (
<>
@@ -474,6 +483,9 @@ export const LoadingStates: Story = {
setHasData((prev) => !prev)}>
Empty data
+ setCustomNoData((prev) => !prev)}>
+ Custom NoDataComponent
+
diff --git a/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/FeatureExamples.mdx b/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/FeatureExamples.mdx
index d3df4365ed1..d364d9ad302 100644
--- a/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/FeatureExamples.mdx
+++ b/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/FeatureExamples.mdx
@@ -378,7 +378,20 @@ const ServerSideTable = () => {
The table supports multiple loading indicators: a skeleton placeholder (when `loading` is true and data is empty), a `BusyIndicator` overlay (when `loading` is true and data exists), and `showOverlay` which displays a semi-transparent overlay without a loading indicator. When `loading` is active, user interactions are blocked as well. Set `alwaysShowBusyIndicator` to always show the BusyIndicator instead of the skeleton.
-**Recommended:** Set `alwaysShowBusyIndicator` to `true` in most cases. The default skeleton loading indicator is only sufficient when loading times exceed 1 second. See [Fiori Skeleton Loading](https://www.sap.com/design-system/fiori-design-ios/ui-elements/patterns/skeleton-loading/?external) for design guidance.
+**Recommended:** Set `alwaysShowBusyIndicator` to `true` in most cases. The default skeleton loading indicator should only be used when loading times exceed 1 second. See [Fiori Skeleton Loading](https://www.sap.com/design-system/fiori-design-ios/v26-4/components/progress-indicators/skeleton-loading) for design guidance.
+
+The following table summarizes which indicator is rendered for the empty and non-empty states:
+
+| `loading` | `data` | `alwaysShowBusyIndicator` | Result |
+| --------- | ------ | ------------------------- | ------------------------------------------------------------------------- |
+| `true` | filled | `false` | `BusyIndicator` overlay on top of the existing rows |
+| `true` | empty | `false` | Skeleton loader (`TablePlaceholder`) — `NoDataComponent` is **not** shown |
+| `true` | empty | `true` | `BusyIndicator` overlay on top of the `NoDataComponent` |
+| `false` | empty | any | `NoDataComponent` only |
+
+The `BusyIndicator` is an overlay: it is layered on top of the table and dims the content beneath it, but it does not replace the normal render logic. This is why, with `alwaysShowBusyIndicator` and empty data, the `NoDataComponent` is rendered underneath the overlay while loading — there is no skeleton loader in this mode by design.
+
+For the default text `NoDataComponent` this is unobtrusive, but a richer custom empty state — like an `IllustratedMessage` — would flash beneath the overlay during the initial load, before any data has arrived. To avoid this, render the custom component only once loading has finished and fall back to the default "No data" placeholder in the meantime by returning `undefined`. Toggle **Custom NoDataComponent**, **loading**, and **alwaysShowBusyIndicator** in the example below to compare the patterns.
@@ -396,6 +409,13 @@ The table supports multiple loading indicators: a skeleton placeholder (when `lo
// Overlay without loading indicator
+
+// Custom NoDataComponent (e.g. IllustratedMessage) with alwaysShowBusyIndicator:
+// guard it so the illustration doesn't flash beneath the overlay while loading.
+const NoDataComponent = loading
+ ? undefined // fall back to the default "No data" placeholder during loading
+ : (props) => ;
+
```
## Controlled Selection
diff --git a/packages/main/src/components/AnalyticalTable/types/index.ts b/packages/main/src/components/AnalyticalTable/types/index.ts
index 031e1815827..ae852be9ba0 100644
--- a/packages/main/src/components/AnalyticalTable/types/index.ts
+++ b/packages/main/src/components/AnalyticalTable/types/index.ts
@@ -1179,12 +1179,13 @@ export interface AnalyticalTablePropTypes extends Omit {
*/
onFilter?: (e: OnFilterParam) => void;
/**
- * Component that will be rendered when the table is not loading and has no data.
+ * Component that will be rendered when the table has no data.
*
* __Note:__
* - Although this prop accepts all React components, it is strongly recommended that you use `IllustratedMessage` with `design="Auto"` to preserve the intended design.
* - For `visibleRowCountMode`s "Auto" and "AutoWithEmptyRows", the `NoDataComponent` spreads to the available space below the header row, otherwise the `minRows` prop defines the minimum height of the table body.
* - To prevent overflow, ensure the table body is at least **60 px** tall when displaying an `IllustratedMessage`.
+ * - While loading, it's only rendered if `alwaysShowBusyIndicator` is set. To hide the custom NoDataComponent during loading, guard it: `NoDataComponent={loading ? undefined : MyNoDataComponent}`.
*
* @default DefaultNoDataComponent
*/