diff --git a/src/ui/src/components/home_page/home_page.ts b/src/ui/src/components/home_page/home_page.ts
index 50f3ab28..61941ef2 100644
--- a/src/ui/src/components/home_page/home_page.ts
+++ b/src/ui/src/components/home_page/home_page.ts
@@ -46,6 +46,7 @@ import {
   SETTING_DISALLOW_VERTICAL_EDGE_LABELS,
   SETTING_EDGE_COLOR,
   SETTING_EDGE_LABEL_FONT_SIZE,
+  SETTING_HIDE_EMPTY_NODE_DATA_ENTRIES,
   SETTING_HIDE_OP_NODES_WITH_LABELS,
   SETTING_HIGHLIGHT_LAYER_NODE_INPUTS_OUTPUTS,
   SETTING_KEEP_LAYERS_WITH_A_SINGLE_CHILD,
@@ -355,6 +356,9 @@ export class HomePage implements AfterViewInit {
       highlightLayerNodeInputsOutputs: this.settingsService.getBooleanValue(
         SETTING_HIGHLIGHT_LAYER_NODE_INPUTS_OUTPUTS,
       ),
+      hideEmptyNodeDataEntries: this.settingsService.getBooleanValue(
+        SETTING_HIDE_EMPTY_NODE_DATA_ENTRIES,
+      ),
     };
   }
 
diff --git a/src/ui/src/components/visualizer/common/utils.ts b/src/ui/src/components/visualizer/common/utils.ts
index 7034088d..e29ce449 100644
--- a/src/ui/src/components/visualizer/common/utils.ts
+++ b/src/ui/src/components/visualizer/common/utils.ts
@@ -55,6 +55,7 @@ import {
   ShowOnNodeItemData,
   ShowOnNodeItemType,
 } from './types';
+import {VisualizerConfig} from './visualizer_config';
 
 const CANVAS = new OffscreenCanvas(300, 300);
 
@@ -530,6 +531,7 @@ export function getOpNodeDataProviderKeyValuePairsForAttrsTable(
   modelGraphId: string,
   showOnNodeItemTypes: Record<string, ShowOnNodeItemData>,
   curNodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
+  config?: VisualizerConfig,
 ): KeyValueList {
   const keyValuePairs: KeyValueList = [];
   const runNames = Object.keys(showOnNodeItemTypes)
@@ -544,7 +546,11 @@ export function getOpNodeDataProviderKeyValuePairsForAttrsTable(
     runNames.includes(getRunName(run, {id: modelGraphId})),
   );
   for (const run of runs) {
-    const value = (run.results || {})?.[modelGraphId][node.id]?.strValue || '-';
+    const result = (run.results || {})?.[modelGraphId]?.[node.id];
+    if (config?.hideEmptyNodeDataEntries && !result) {
+      continue;
+    }
+    const value = result?.strValue || '-';
     keyValuePairs.push({
       key: getRunName(run, {id: modelGraphId}),
       value,
diff --git a/src/ui/src/components/visualizer/common/visualizer_config.ts b/src/ui/src/components/visualizer/common/visualizer_config.ts
index 83ccc290..b87b5f94 100644
--- a/src/ui/src/components/visualizer/common/visualizer_config.ts
+++ b/src/ui/src/components/visualizer/common/visualizer_config.ts
@@ -61,6 +61,9 @@ export declare interface VisualizerConfig {
   /** Whether to highlight layer node inputs and outputs. */
   highlightLayerNodeInputsOutputs?: boolean;
 
+  /** Whether to hide empty node data entries. */
+  hideEmptyNodeDataEntries?: boolean;
+
   /** The default node styler rules. */
   nodeStylerRules?: NodeStylerRule[];
 
diff --git a/src/ui/src/components/visualizer/common/worker_events.ts b/src/ui/src/components/visualizer/common/worker_events.ts
index 2a6a390a..fea5340a 100644
--- a/src/ui/src/components/visualizer/common/worker_events.ts
+++ b/src/ui/src/components/visualizer/common/worker_events.ts
@@ -85,6 +85,7 @@ export declare interface ExpandOrCollapseGroupNodeRequest
   all?: boolean;
   // Timestamp of when the request is sent.
   ts?: number;
+  config?: VisualizerConfig;
 }
 
 /** The response for expanding/collapsing a group node. */
@@ -119,6 +120,7 @@ export declare interface RelayoutGraphRequest extends WorkerEventBase {
   forRestoringSnapshotAfterTogglingFlattenLayers?: boolean;
   nodeStylerQueries?: NodeStylerRule[];
   triggerNavigationSync?: boolean;
+  config?: VisualizerConfig;
 }
 
 /** The response for re-laying out the whole graph. */
@@ -144,6 +146,7 @@ export declare interface LocateNodeRequest extends WorkerEventBase {
   rendererId: string;
   noNodeShake?: boolean;
   select?: boolean;
+  config?: VisualizerConfig;
 }
 
 /** The response for locating a node. */
diff --git a/src/ui/src/components/visualizer/info_panel.ng.html b/src/ui/src/components/visualizer/info_panel.ng.html
index 3851712c..bab49db0 100644
--- a/src/ui/src/components/visualizer/info_panel.ng.html
+++ b/src/ui/src/components/visualizer/info_panel.ng.html
@@ -17,33 +17,36 @@
 -->
 
 <div class="container" [class.graph-info]="showNodeDataProviderSummary">
-  <div class="section" #sectionEle
-       [class.collapsed]="isSectionCollapsed(section.label)"
-       *ngFor="let section of sections; trackBy: trackBySectionLabel">
-    <div class="header">
-      <button mat-icon-button class="toggle"
-          (click)="handleToggleSection(section.label, sectionEle)">
-        <mat-icon>{{getSectionToggleIcon(section.label)}}</mat-icon>
-      </button>
-      {{section.label}}
-    </div>
-    <div class="items-container">
-      <table class="metadata-table info-attrs">
-        @for (item of section.items; track item.id || item.label) {
-          <tr [class.search-match]="isSearchMatchedAttrId(item.label)">
-            <td class="key"><hoverable-label [label]="item.label"></hoverable-label></td>
-            <td class="value">
-              <expandable-info-text
-                  [text]="item.value" [type]="item.label"
-                  [bgColor]="item.bgColor || 'transparent'"
-                  [textColor]="item.textColor || 'black'">
-              </expandable-info-text>
-            </td>
-          </tr>
-        }
-      </table>
-    </div>
-  </div>
+  @for (section of sections; track section.label) {
+    @if (section.items.length > 0) {
+      <div class="section" #sectionEle
+          [class.collapsed]="isSectionCollapsed(section.label)">
+        <div class="header">
+          <button mat-icon-button class="toggle"
+              (click)="handleToggleSection(section.label, sectionEle)">
+            <mat-icon>{{getSectionToggleIcon(section.label)}}</mat-icon>
+          </button>
+          {{section.label}}
+        </div>
+        <div class="items-container">
+          <table class="metadata-table info-attrs">
+            @for (item of section.items; track item.id || item.label) {
+              <tr [class.search-match]="isSearchMatchedAttrId(item.label)">
+                <td class="key"><hoverable-label [label]="item.label"></hoverable-label></td>
+                <td class="value">
+                  <expandable-info-text
+                      [text]="item.value" [type]="item.label"
+                      [bgColor]="item.bgColor || 'transparent'"
+                      [textColor]="item.textColor || 'black'">
+                  </expandable-info-text>
+                </td>
+              </tr>
+            }
+          </table>
+        </div>
+      </div>
+    }
+  }
 
   <!-- Summary for node data provider extensions -->
   <div class="section" *ngIf="showNodeDataProviderSummary" #ndpSectionEle
diff --git a/src/ui/src/components/visualizer/info_panel.ts b/src/ui/src/components/visualizer/info_panel.ts
index 9aacfff9..dd80d795 100644
--- a/src/ui/src/components/visualizer/info_panel.ts
+++ b/src/ui/src/components/visualizer/info_panel.ts
@@ -559,10 +559,6 @@ export class InfoPanel {
     return (connectedNodesMetadataItem?.connectedNodes || []).length > 0;
   }
 
-  trackBySectionLabel(index: number, section: InfoSection): string {
-    return section.label;
-  }
-
   trackByItemIdOrLabel(index: number, item: InfoItem): string {
     return item.id || item.label;
   }
@@ -798,6 +794,9 @@ export class InfoPanel {
         const nodeResult = ((run.results || {})[this.curModelGraph.id] || {})[
           opNode.id
         ];
+        if (this.appService.config()?.hideEmptyNodeDataEntries && !nodeResult) {
+          continue;
+        }
         const strValue = nodeResult?.strValue || '-';
         const bgColor = nodeResult?.bgColor || 'transparent';
         const textColor = nodeResult?.textColor || 'black';
diff --git a/src/ui/src/components/visualizer/webgl_renderer.ts b/src/ui/src/components/visualizer/webgl_renderer.ts
index c87ea1e6..d8ab57c9 100644
--- a/src/ui/src/components/visualizer/webgl_renderer.ts
+++ b/src/ui/src/components/visualizer/webgl_renderer.ts
@@ -1393,6 +1393,7 @@ export class WebglRenderer implements OnInit, OnDestroy {
         rendererId,
         noNodeShake,
         select,
+        config: this.appService.config(),
       };
       this.workerService.worker.postMessage(req);
     }
@@ -1423,6 +1424,7 @@ export class WebglRenderer implements OnInit, OnDestroy {
       clearAllExpandStates,
       forRestoringSnapshotAfterTogglingFlattenLayers,
       triggerNavigationSync,
+      config: this.appService.config(),
     };
     this.workerService.worker.postMessage(req);
   }
@@ -1886,6 +1888,7 @@ export class WebglRenderer implements OnInit, OnDestroy {
       paneId: this.paneId,
       all,
       ts: Date.now(),
+      config: this.appService.config(),
     };
     this.workerService.worker.postMessage(req);
   }
diff --git a/src/ui/src/components/visualizer/webgl_renderer_attrs_table_service.ts b/src/ui/src/components/visualizer/webgl_renderer_attrs_table_service.ts
index e04454fd..a772bad0 100644
--- a/src/ui/src/components/visualizer/webgl_renderer_attrs_table_service.ts
+++ b/src/ui/src/components/visualizer/webgl_renderer_attrs_table_service.ts
@@ -164,6 +164,7 @@ export class WebglRendererAttrsTableService {
             this.webglRenderer.curModelGraph.id,
             this.webglRenderer.curShowOnNodeItemTypes,
             this.webglRenderer.curNodeDataProviderRuns,
+            this.webglRenderer.appService.config(),
           ),
         );
       } else if (isGroupNode(node)) {
diff --git a/src/ui/src/components/visualizer/worker/graph_expander.ts b/src/ui/src/components/visualizer/worker/graph_expander.ts
index 4ecd3fd2..db658ca0 100644
--- a/src/ui/src/components/visualizer/worker/graph_expander.ts
+++ b/src/ui/src/components/visualizer/worker/graph_expander.ts
@@ -29,6 +29,7 @@ import {
   isGroupNode,
   splitLabel,
 } from '../common/utils';
+import {VisualizerConfig} from '../common/visualizer_config';
 
 import {Dagre, DagreGraphInstance} from './dagre_types';
 import {
@@ -55,6 +56,7 @@ export class GraphExpander {
       NodeDataProviderRunData
     >,
     private readonly testMode = false,
+    private readonly config?: VisualizerConfig,
   ) {}
 
   /** Expands the given group node to show its child nodes. */
@@ -85,6 +87,8 @@ export class GraphExpander {
         this.dagre,
         this.showOnNodeItemTypes,
         this.nodeDataProviderRuns,
+        this.testMode,
+        this.config,
       );
       const rect = layout.layout(curGroupNodeId);
       if (this.testMode) {
@@ -107,6 +111,8 @@ export class GraphExpander {
       this.dagre,
       this.showOnNodeItemTypes,
       this.nodeDataProviderRuns,
+      this.testMode,
+      this.config,
     );
     layout.layout();
     if (this.testMode) {
@@ -161,6 +167,8 @@ export class GraphExpander {
         this.dagre,
         this.showOnNodeItemTypes,
         this.nodeDataProviderRuns,
+        this.testMode,
+        this.config,
       );
       const rect = layout.layout(groupNodeId);
       if (this.testMode) {
@@ -180,6 +188,8 @@ export class GraphExpander {
       this.dagre,
       this.showOnNodeItemTypes,
       this.nodeDataProviderRuns,
+      this.testMode,
+      this.config,
     );
     layout.layout();
     if (this.testMode) {
@@ -246,6 +256,7 @@ export class GraphExpander {
       this.nodeDataProviderRuns,
       this.testMode,
       true,
+      this.config,
     );
 
     // From the given group node's parent, layout, update size, and continue to
@@ -265,6 +276,8 @@ export class GraphExpander {
         this.dagre,
         this.showOnNodeItemTypes,
         this.nodeDataProviderRuns,
+        this.testMode,
+        this.config,
       );
       const rect = layout.layout(curGroupNodeId);
       if (this.testMode) {
@@ -287,6 +300,8 @@ export class GraphExpander {
       this.dagre,
       this.showOnNodeItemTypes,
       this.nodeDataProviderRuns,
+      this.testMode,
+      this.config,
     );
     layout.layout();
     if (this.testMode) {
@@ -346,6 +361,8 @@ export class GraphExpander {
         this.dagre,
         this.showOnNodeItemTypes,
         this.nodeDataProviderRuns,
+        this.testMode,
+        this.config,
       );
       layout.layout();
     }
@@ -384,6 +401,8 @@ export class GraphExpander {
       this.dagre,
       this.showOnNodeItemTypes,
       this.nodeDataProviderRuns,
+      this.testMode,
+      this.config,
     );
     layout.layout();
 
diff --git a/src/ui/src/components/visualizer/worker/graph_layout.ts b/src/ui/src/components/visualizer/worker/graph_layout.ts
index dcc6f2ea..fc97a8f9 100644
--- a/src/ui/src/components/visualizer/worker/graph_layout.ts
+++ b/src/ui/src/components/visualizer/worker/graph_layout.ts
@@ -60,6 +60,7 @@ import {
   isOpNode,
   splitLabel,
 } from '../common/utils';
+import {VisualizerConfig} from '../common/visualizer_config';
 
 import {Dagre, DagreGraphInstance} from './dagre_types';
 
@@ -114,6 +115,7 @@ export class GraphLayout {
       NodeDataProviderRunData
     >,
     private readonly testMode = false,
+    private readonly config?: VisualizerConfig,
   ) {
     this.dagreGraph = new this.dagre.graphlib.Graph();
   }
@@ -143,6 +145,8 @@ export class GraphLayout {
       this.showOnNodeItemTypes,
       this.nodeDataProviderRuns,
       this.testMode,
+      false,
+      this.config,
     );
 
     // Set nodes/edges to dagre.
@@ -259,6 +263,8 @@ export class GraphLayout {
         this.modelGraph,
         this.showOnNodeItemTypes,
         this.nodeDataProviderRuns,
+        this.testMode,
+        this.config,
       );
       if (subgraphFullWidth < parentNodeWidth) {
         const extraOffsetX = (parentNodeWidth - subgraphFullWidth) / 2;
@@ -318,6 +324,7 @@ export function getNodeWidth(
   showOnNodeItemTypes: Record<string, ShowOnNodeItemData>,
   nodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
   testMode = false,
+  config?: VisualizerConfig,
 ) {
   // Always return 32 in test mode.
   if (testMode) {
@@ -404,6 +411,7 @@ export function getNodeWidth(
         modelGraph.id,
         showOnNodeItemTypes,
         nodeDataProviderRuns,
+        config,
       );
     const nodeDataProviderWidths = getMaxAttrLabelAndValueWidth(
       nodeDataProviderKeyValuePairs,
@@ -475,6 +483,7 @@ export function getNodeHeight(
   nodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
   testMode = false,
   forceRecalculate = false,
+  config?: VisualizerConfig,
 ) {
   if (testMode) {
     return DEFAULT_NODE_HEIGHT;
@@ -495,6 +504,7 @@ export function getNodeHeight(
       node,
       nodeDataProviderRuns,
       modelGraph,
+      config,
     );
   } else if (isGroupNode(node)) {
     attrsTableRowCount = getGroupNodeAttrsTableRowCount(
@@ -521,6 +531,7 @@ export function getLayoutGraph(
   nodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
   testMode = false,
   useFakeNodeSize = false,
+  config?: VisualizerConfig,
 ): LayoutGraph {
   const layoutGraph: LayoutGraph = {
     nodes: {},
@@ -545,6 +556,7 @@ export function getLayoutGraph(
               showOnNodeItemTypes,
               nodeDataProviderRuns,
               testMode,
+              config,
             )),
       height: useFakeNodeSize
         ? 10
@@ -554,6 +566,8 @@ export function getLayoutGraph(
             showOnNodeItemTypes,
             nodeDataProviderRuns,
             testMode,
+            false,
+            config,
           ),
       config: isOpNode(node) ? node.config : undefined,
     };
@@ -586,6 +600,7 @@ function getOpNodeAttrsTableRowCount(
   node: OpNode,
   nodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
   modelGraph: ModelGraph,
+  config?: VisualizerConfig,
 ): number {
   // Basic info fields.
   const baiscFieldIds =
@@ -624,14 +639,19 @@ function getOpNodeAttrsTableRowCount(
         showOnNodeItemType.startsWith(
           NODE_DATA_PROVIDER_SHOW_ON_NODE_TYPE_PREFIX,
         ) &&
-        Object.values(nodeDataProviderRuns).some(
-          (run) =>
+        Object.values(nodeDataProviderRuns).some((run) => {
+          const result = (run.results || {})?.[modelGraph.id]?.[node.id];
+          if (config?.hideEmptyNodeDataEntries && !result) {
+            return false;
+          }
+          return (
             getRunName(run, modelGraph) ===
             showOnNodeItemType.replace(
               NODE_DATA_PROVIDER_SHOW_ON_NODE_TYPE_PREFIX,
               '',
-            ),
-        ),
+            )
+          );
+        }),
     ).length;
 
   return (
diff --git a/src/ui/src/components/visualizer/worker/graph_processor.ts b/src/ui/src/components/visualizer/worker/graph_processor.ts
index 4691dff8..c418e7d7 100644
--- a/src/ui/src/components/visualizer/worker/graph_processor.ts
+++ b/src/ui/src/components/visualizer/worker/graph_processor.ts
@@ -484,6 +484,7 @@ export class GraphProcessor {
           this.testMode,
           // Use fake node size.
           true,
+          this.config,
         );
 
         // Find root nodes of the layout graph.
diff --git a/src/ui/src/components/visualizer/worker/worker.ts b/src/ui/src/components/visualizer/worker/worker.ts
index 532e87db..dbf5dd58 100644
--- a/src/ui/src/components/visualizer/worker/worker.ts
+++ b/src/ui/src/components/visualizer/worker/worker.ts
@@ -113,6 +113,7 @@ self.addEventListener('message', (event: Event) => {
           workerEvent.showOnNodeItemTypes,
           workerEvent.nodeDataProviderRuns,
           workerEvent.all === true,
+          workerEvent.config,
         );
       } else {
         deepestExpandedGroupNodeIds = handleCollapseGroupNode(
@@ -121,6 +122,7 @@ self.addEventListener('message', (event: Event) => {
           workerEvent.showOnNodeItemTypes,
           workerEvent.nodeDataProviderRuns,
           workerEvent.all === true,
+          workerEvent.config,
         );
       }
       cacheModelGraph(modelGraph, workerEvent.rendererId);
@@ -146,6 +148,7 @@ self.addEventListener('message', (event: Event) => {
         workerEvent.nodeDataProviderRuns,
         workerEvent.targetDeepestGroupNodeIdsToExpand,
         workerEvent.clearAllExpandStates,
+        workerEvent.config,
       );
       cacheModelGraph(modelGraph, workerEvent.rendererId);
       const resp: RelayoutGraphResponse = {
@@ -174,6 +177,7 @@ self.addEventListener('message', (event: Event) => {
         workerEvent.showOnNodeItemTypes,
         workerEvent.nodeDataProviderRuns,
         workerEvent.nodeId,
+        workerEvent.config,
       );
       cacheModelGraph(modelGraph, workerEvent.rendererId);
       const resp: LocateNodeResponse = {
@@ -264,12 +268,15 @@ function handleExpandGroupNode(
   showOnNodeItemTypes: Record<string, ShowOnNodeItemData>,
   nodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
   all: boolean,
+  config?: VisualizerConfig,
 ): string[] {
   const expander = new GraphExpander(
     modelGraph,
     dagre,
     showOnNodeItemTypes,
     nodeDataProviderRuns,
+    false,
+    config,
   );
 
   // Expane group node.
@@ -337,12 +344,15 @@ function handleCollapseGroupNode(
   showOnNodeItemTypes: Record<string, ShowOnNodeItemData>,
   nodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
   all: boolean,
+  config?: VisualizerConfig,
 ): string[] {
   const expander = new GraphExpander(
     modelGraph,
     dagre,
     showOnNodeItemTypes,
     nodeDataProviderRuns,
+    false,
+    config,
   );
 
   if (groupNodeId != null) {
@@ -370,12 +380,15 @@ function handleReLayoutGraph(
   nodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
   targetDeepestGroupNodeIdsToExpand?: string[],
   clearAllExpandStates?: boolean,
+  config?: VisualizerConfig,
 ) {
   const expander = new GraphExpander(
     modelGraph,
     dagre,
     showOnNodeItemTypes,
     nodeDataProviderRuns,
+    false,
+    config,
   );
   expander.reLayoutGraph(
     targetDeepestGroupNodeIdsToExpand,
@@ -388,12 +401,15 @@ function handleLocateNode(
   showOnNodeItemTypes: Record<string, ShowOnNodeItemData>,
   nodeDataProviderRuns: Record<string, NodeDataProviderRunData>,
   nodeId: string,
+  config?: VisualizerConfig,
 ): string[] {
   const expander = new GraphExpander(
     modelGraph,
     dagre,
     showOnNodeItemTypes,
     nodeDataProviderRuns,
+    false,
+    config,
   );
   return expander.expandToRevealNode(nodeId);
 }
diff --git a/src/ui/src/services/settings_service.ts b/src/ui/src/services/settings_service.ts
index 0206eb0f..d37c31ae 100644
--- a/src/ui/src/services/settings_service.ts
+++ b/src/ui/src/services/settings_service.ts
@@ -36,6 +36,7 @@ export enum SettingKey {
   KEEP_LAYERS_WITH_A_SINGLE_CHILD = 'keep_layers_with_a_single_child',
   SHOW_OP_NODE_OUT_OF_LAYER_EDGES_WITHOUT_SELECTING = 'show_op_node_out_of_layer_edges_without_selecting',
   HIGHLIGHT_LAYER_NODE_INPUTS_OUTPUTS = 'highlight_layer_node_inputs_outputs',
+  HIDE_EMPTY_NODE_DATA_ENTRIES = 'hide_empty_node_data_entries',
 }
 
 /** Setting types. */
@@ -170,6 +171,17 @@ export const SETTING_HIGHLIGHT_LAYER_NODE_INPUTS_OUTPUTS: Setting = {
     'that layer.',
 };
 
+/** Settings for hiding empty noda data entries. */
+export const SETTING_HIDE_EMPTY_NODE_DATA_ENTRIES: Setting = {
+  label: 'Hide node data entries with empty values',
+  key: SettingKey.HIDE_EMPTY_NODE_DATA_ENTRIES,
+  type: SettingType.BOOLEAN,
+  defaultValue: false,
+  help:
+    'Enable this setting to hide node data entries ' +
+    '(on node overlay and in side panel) with empty values.',
+};
+
 const SETTINGS_LOCAL_STORAGE_KEY = 'model_explorer_settings';
 
 /** All settings. */
@@ -184,6 +196,7 @@ export const ALL_SETTINGS = [
   SETTING_DISALLOW_VERTICAL_EDGE_LABELS,
   SETTING_MAX_CONST_ELEMENT_COUNT_LIMIT,
   SETTING_HIGHLIGHT_LAYER_NODE_INPUTS_OUTPUTS,
+  SETTING_HIDE_EMPTY_NODE_DATA_ENTRIES,
 ];
 
 /**