diff --git a/components/menu/demo/horizontal.vue b/components/menu/demo/horizontal.vue
index 9038f81d9..19cf9af9b 100644
--- a/components/menu/demo/horizontal.vue
+++ b/components/menu/demo/horizontal.vue
@@ -22,8 +22,9 @@ Horizontal top navigation menu.
 <script lang="ts" setup>
 import { h, ref } from 'vue';
 import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons-vue';
+import { MenuProps } from 'ant-design-vue';
 const current = ref<string[]>(['mail']);
-const items = ref([
+const items = ref<MenuProps['items']>([
   {
     key: 'mail',
     icon: () => h(MailOutlined),
diff --git a/components/menu/index.en-US.md b/components/menu/index.en-US.md
index b497abab0..69d319671 100644
--- a/components/menu/index.en-US.md
+++ b/components/menu/index.en-US.md
@@ -66,14 +66,14 @@ More layouts with navigation: [Layout](/components/layout).
 
 #### MenuItemType
 
-| Param    | Description                          | Type    | Default value | Version |
-| -------- | ------------------------------------ | ------- | ------------- | ------- |
-| danger   | Display the danger style             | boolean | false         |         |
-| disabled | Whether menu item is disabled        | boolean | false         |         |
-| icon     | The icon of the menu item            | VueNode | -             |         |
-| key      | Unique ID of the menu item           | string  | -             |         |
-| label    | Menu label                           | VueNode | -             |         |
-| title    | Set display title for collapsed item | string  | -             |         |
+| Param | Description | Type | Default value | Version |
+| --- | --- | --- | --- | --- |
+| danger | Display the danger style | boolean | false |  |
+| disabled | Whether menu item is disabled | boolean | false |  |
+| icon | The icon of the menu item | VueNode \| (item: MenuItemType) => VNode | - |  |
+| key | Unique ID of the menu item | string | - |  |
+| label | Menu label | VueNode | - |  |
+| title | Set display title for collapsed item | string | - |  |
 
 #### SubMenuType
 
@@ -82,7 +82,7 @@ More layouts with navigation: [Layout](/components/layout).
 | --- | --- | --- | --- | --- |
 | children | Sub-menus or sub-menu items | [ItemType\[\]](#ItemType) | - |  |
 | disabled | Whether sub-menu is disabled | boolean | false |  |
-| icon | Icon of sub menu | VueNode\|()=>VueNode | - |  |
+| icon | Icon of sub menu | VueNode \| (item: SubMenuType) => VueNode | - |  |
 | key | Unique ID of the sub-menu | string | - |  |
 | label | Menu label | VueNode | - |  |
 | popupClassName | Sub-menu class name, not working when `mode="inline"` | string | - |  |
diff --git a/components/menu/index.zh-CN.md b/components/menu/index.zh-CN.md
index ecc0f3908..08202d2ba 100644
--- a/components/menu/index.zh-CN.md
+++ b/components/menu/index.zh-CN.md
@@ -67,14 +67,14 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Vn4XSqJFAxcAAA
 
 ### MenuItemType
 
-| 参数     | 说明                     | 类型                 | 默认值 | 版本 |
-| -------- | ------------------------ | -------------------- | ------ | ---- |
-| danger   | 展示错误状态样式         | boolean              | false  |      |
-| disabled | 是否禁用                 | boolean              | false  |      |
-| icon     | 菜单图标                 | VueNode\|()=>VueNode | -      |      |
-| key      | item 的唯一标志          | string               | -      |      |
-| label    | 菜单项标题               | VueNode              | -      |      |
-| title    | 设置收缩时展示的悬浮标题 | string               | -      |      |
+| 参数     | 说明                     | 类型                                   | 默认值 | 版本 |
+| -------- | ------------------------ | -------------------------------------- | ------ | ---- |
+| danger   | 展示错误状态样式         | boolean                                | false  |      |
+| disabled | 是否禁用                 | boolean                                | false  |      |
+| icon     | 菜单图标                 | VueNode\|(item: MenuItemType)=>VueNode | -      |      |
+| key      | item 的唯一标志          | string                                 | -      |      |
+| label    | 菜单项标题               | VueNode                                | -      |      |
+| title    | 设置收缩时展示的悬浮标题 | string                                 | -      |      |
 
 #### SubMenuType
 
@@ -82,7 +82,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Vn4XSqJFAxcAAA
 | --- | --- | --- | --- | --- |
 | children | 子菜单的菜单项 | [ItemType\[\]](#ItemType) | - |  |
 | disabled | 是否禁用 | boolean | false |  |
-| icon | 菜单图标 | VueNode\|()=>VueNode | - |  |
+| icon | 菜单图标 | VueNode\|(item: SubMenuType)=>VueNode | - |  |
 | key | 唯一标志 | string | - |  |
 | label | 菜单项标题 | VueNode | - |  |
 | popupClassName | 子菜单样式,`mode="inline"` 时无效 | string | - |  |
diff --git a/components/menu/src/ItemGroup.tsx b/components/menu/src/ItemGroup.tsx
index 875decd58..970ccfe8d 100644
--- a/components/menu/src/ItemGroup.tsx
+++ b/components/menu/src/ItemGroup.tsx
@@ -4,9 +4,13 @@ import { computed, defineComponent } from 'vue';
 import PropTypes from '../../_util/vue-types';
 import { useInjectMenu } from './hooks/useMenuContext';
 import { useMeasure } from './hooks/useKeyPath';
+import type { ItemType } from './interface';
+import { objectType } from '../../_util/type';
 
 export const menuItemGroupProps = () => ({
   title: PropTypes.any,
+  // Internal user prop
+  originItemValue: objectType<ItemType>(),
 });
 
 export type MenuItemGroupProps = Partial<ExtractPropTypes<ReturnType<typeof menuItemGroupProps>>>;
diff --git a/components/menu/src/MenuItem.tsx b/components/menu/src/MenuItem.tsx
index 1c459683a..ce741a433 100644
--- a/components/menu/src/MenuItem.tsx
+++ b/components/menu/src/MenuItem.tsx
@@ -1,4 +1,4 @@
-import { flattenChildren, getPropsSlot, isValidElement } from '../../_util/props-util';
+import { flattenChildren, isValidElement } from '../../_util/props-util';
 import PropTypes from '../../_util/vue-types';
 import type { ExtractPropTypes, PropType } from 'vue';
 import {
@@ -13,12 +13,13 @@ import { useInjectKeyPath, useMeasure } from './hooks/useKeyPath';
 import { useInjectFirstLevel, useInjectMenu } from './hooks/useMenuContext';
 import { cloneElement } from '../../_util/vnode';
 import Tooltip from '../../tooltip';
-import type { MenuInfo } from './interface';
+import type { ItemType, MenuInfo } from './interface';
 import KeyCode from '../../_util/KeyCode';
 import useDirectionStyle from './hooks/useDirectionStyle';
 import Overflow from '../../vc-overflow';
 import devWarning from '../../vc-util/devWarning';
 import type { MouseEventHandler } from '../../_util/EventInterface';
+import { objectType } from '../../_util/type';
 
 let indexGuid = 0;
 export const menuItemProps = () => ({
@@ -33,6 +34,8 @@ export const menuItemProps = () => ({
   onClick: Function as PropType<MouseEventHandler>,
   onKeydown: Function as PropType<MouseEventHandler>,
   onFocus: Function as PropType<MouseEventHandler>,
+  // Internal user prop
+  originItemValue: objectType<ItemType>(),
 });
 
 export type MenuItemProps = Partial<ExtractPropTypes<ReturnType<typeof menuItemProps>>>;
@@ -215,7 +218,7 @@ export default defineComponent({
         optionRoleProps['aria-selected'] = selected.value;
       }
 
-      const icon = getPropsSlot(slots, props, 'icon');
+      const icon = props.icon ?? slots.icon?.(props);
       return (
         <Tooltip
           {...tooltipProps}
@@ -248,7 +251,7 @@ export default defineComponent({
             title={typeof title === 'string' ? title : undefined}
           >
             {cloneElement(
-              typeof icon === 'function' ? icon() : icon,
+              typeof icon === 'function' ? icon(props.originItemValue) : icon,
               {
                 class: `${prefixCls.value}-item-icon`,
               },
diff --git a/components/menu/src/SubMenu.tsx b/components/menu/src/SubMenu.tsx
index b9bae5b87..54c94fc1a 100644
--- a/components/menu/src/SubMenu.tsx
+++ b/components/menu/src/SubMenu.tsx
@@ -28,7 +28,8 @@ import devWarning from '../../vc-util/devWarning';
 import isValid from '../../_util/isValid';
 import type { MouseEventHandler } from '../../_util/EventInterface';
 import type { Key } from '../../_util/type';
-import type { MenuTheme } from './interface';
+import { objectType } from '../../_util/type';
+import type { ItemType, MenuTheme } from './interface';
 
 let indexGuid = 0;
 
@@ -46,6 +47,9 @@ export const subMenuProps = () => ({
   onMouseenter: Function as PropType<MouseEventHandler>,
   onMouseleave: Function as PropType<MouseEventHandler>,
   onTitleClick: Function as PropType<(e: MouseEvent, key: Key) => void>,
+
+  // Internal user prop
+  originItemValue: objectType<ItemType>(),
 });
 
 export type SubMenuProps = Partial<ExtractPropTypes<ReturnType<typeof subMenuProps>>>;
@@ -224,7 +228,7 @@ export default defineComponent({
       return (
         <>
           {cloneElement(
-            typeof icon === 'function' ? icon() : icon,
+            typeof icon === 'function' ? icon(props.originItemValue) : icon,
             {
               class: `${prefixCls.value}-item-icon`,
             },
@@ -247,7 +251,7 @@ export default defineComponent({
     );
     const baseTitleNode = () => {
       const subMenuPrefixClsValue = subMenuPrefixCls.value;
-      const icon = getPropsSlot(slots, props, 'icon');
+      const icon = props.icon ?? slots.icon?.(props);
       const expandIcon = props.expandIcon || slots.expandIcon || menuExpandIcon.value;
       const title = renderTitle(getPropsSlot(slots, props, 'title'), icon);
       return (
diff --git a/components/menu/src/hooks/useItems.tsx b/components/menu/src/hooks/useItems.tsx
index b49127f9d..322493b66 100644
--- a/components/menu/src/hooks/useItems.tsx
+++ b/components/menu/src/hooks/useItems.tsx
@@ -9,19 +9,19 @@ import ItemGroup from '../ItemGroup';
 import MenuDivider from '../Divider';
 import MenuItem from '../MenuItem';
 import type { Key } from '../../../_util/type';
+import type { VNode } from 'vue';
 import { ref, shallowRef, watch } from 'vue';
 import type { MenuProps } from '../Menu';
 import type { StoreMenuInfo } from './useMenuContext';
 
 export interface MenuItemType extends VcMenuItemType {
   danger?: boolean;
-  icon?: any;
+  icon?: VNode | ((item: MenuItemType) => VNode);
   title?: string;
 }
 
 export interface SubMenuType extends Omit<VcSubMenuType, 'children'> {
-  icon?: any;
-  theme?: 'dark' | 'light';
+  icon?: VNode | ((item: SubMenuType) => VNode);
   children: ItemType[];
 }
 
@@ -69,7 +69,7 @@ function convertItemsToNodes(
             const childrenNodes = convertItemsToNodes(children, store, parentMenuInfo);
             // Group
             return (
-              <ItemGroup key={mergedKey} {...restProps} title={label}>
+              <ItemGroup key={mergedKey} {...restProps} title={label} originItemValue={opt}>
                 {childrenNodes}
               </ItemGroup>
             );
@@ -84,7 +84,7 @@ function convertItemsToNodes(
             parentKeys: [].concat(parentKeys, mergedKey),
           });
           return (
-            <SubMenu key={mergedKey} {...restProps} title={label}>
+            <SubMenu key={mergedKey} {...restProps} title={label} originItemValue={opt}>
               {childrenNodes}
             </SubMenu>
           );
@@ -97,7 +97,7 @@ function convertItemsToNodes(
         menuInfo.isLeaf = true;
         store.set(mergedKey, menuInfo);
         return (
-          <MenuItem key={mergedKey} {...restProps}>
+          <MenuItem key={mergedKey} {...restProps} originItemValue={opt}>
             {label}
           </MenuItem>
         );