diff --git a/components/app/__tests__/__snapshots__/demo.test.js.snap b/components/app/__tests__/__snapshots__/demo.test.js.snap
new file mode 100644
index 000000000..cadd37a68
--- /dev/null
+++ b/components/app/__tests__/__snapshots__/demo.test.js.snap
@@ -0,0 +1,41 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders ./components/app/demo/basic.vue correctly 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`renders ./components/app/demo/myPage.vue correctly 1`] = `
+
+
+
+
+
+
+
+
+`;
diff --git a/components/app/__tests__/demo.test.js b/components/app/__tests__/demo.test.js
new file mode 100644
index 000000000..fbcb9a76c
--- /dev/null
+++ b/components/app/__tests__/demo.test.js
@@ -0,0 +1,3 @@
+import demoTest from '../../../tests/shared/demoTest';
+
+demoTest('app');
diff --git a/components/app/context.ts b/components/app/context.ts
new file mode 100644
index 000000000..f4210192c
--- /dev/null
+++ b/components/app/context.ts
@@ -0,0 +1,44 @@
+import { provide, inject, reactive } from 'vue';
+import type { InjectionKey } from 'vue';
+import type { MessageInstance, ConfigOptions as MessageConfig } from '../message/interface';
+import type { NotificationInstance, NotificationConfig } from '../notification/interface';
+import type { ModalStaticFunctions } from '../modal/confirm';
+
+export type AppConfig = {
+ message?: MessageConfig;
+ notification?: NotificationConfig;
+};
+
+export const AppConfigContextKey: InjectionKey = Symbol('appConfigContext');
+
+export const useProvideAppConfigContext = (appConfigContext: AppConfig) => {
+ return provide(AppConfigContextKey, appConfigContext);
+};
+
+export const useInjectAppConfigContext = () => {
+ return inject(AppConfigContextKey, {});
+};
+
+type ModalType = Omit;
+
+export interface useAppProps {
+ message: MessageInstance;
+ notification: NotificationInstance;
+ modal: ModalType;
+}
+
+export const AppContextKey: InjectionKey = Symbol('appContext');
+
+export const useProvideAppContext = (appContext: useAppProps) => {
+ return provide(AppContextKey, appContext);
+};
+
+const defaultAppContext: useAppProps = reactive({
+ message: {},
+ notification: {},
+ modal: {},
+} as useAppProps);
+
+export const useInjectAppContext = () => {
+ return inject(AppContextKey, defaultAppContext);
+};
diff --git a/components/app/demo/basic.vue b/components/app/demo/basic.vue
new file mode 100644
index 000000000..7b0450e49
--- /dev/null
+++ b/components/app/demo/basic.vue
@@ -0,0 +1,26 @@
+
+---
+order: 0
+title:
+ zh-CN: 基本使用
+ en-US: Basic Usage
+---
+
+## zh-CN
+
+获取 `message`、`notification`、`modal` 静态方法。
+
+## en-US
+
+Static method for `message`, `notification`, `modal`.
+
+
+
+
+
+
+
+
+
diff --git a/components/app/demo/index.vue b/components/app/demo/index.vue
new file mode 100644
index 000000000..63a848fd2
--- /dev/null
+++ b/components/app/demo/index.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/components/app/demo/myPage.vue b/components/app/demo/myPage.vue
new file mode 100644
index 000000000..163b5e81b
--- /dev/null
+++ b/components/app/demo/myPage.vue
@@ -0,0 +1,32 @@
+
+
+ Open message
+ Open modal
+ Open notification
+
+
+
+
diff --git a/components/app/index.en-US.md b/components/app/index.en-US.md
new file mode 100644
index 000000000..4c04d1cff
--- /dev/null
+++ b/components/app/index.en-US.md
@@ -0,0 +1,129 @@
+---
+category: Components
+cols: 1
+type: Other
+title: App
+cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
+coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original
+---
+
+Application wrapper for some global usages.
+
+## When To Use
+
+- Provide reset styles based on `.ant-app` element.
+- You could use static methods of `message/notification/Modal` form `useApp` without writing `contextHolder` manually.
+
+## API
+
+### App
+
+| Property | Description | Type | Default | Version |
+| --- | --- | --- | --- | --- |
+| message | Global config for Message | [MessageConfig](/components/message/#messageconfig) | - | 4.x |
+| notification | Global config for Notification | [NotificationConfig](/components/notification/#notificationconfig) | - | 4.x |
+
+## How to use
+
+### Basic usage
+
+App provides upstream and downstream method calls through `provide/inject`, because useApp needs to be used as a subcomponent, we recommend encapsulating App at the top level in the application.
+
+```html
+/*myPage.vue*/
+
+
+ Open message
+ Open modal
+ Open notification
+
+
+
+
+```
+
+Note: App.useApp must be available under App.
+
+#### Embedded usage scenarios (if not necessary, try not to do nesting)
+
+```html
+
+
+ ...
+ ...
+
+
+```
+
+#### Sequence with ConfigProvider
+
+The App component can only use the token in the `ConfigProvider`, if you need to use the Token, the ConfigProvider and the App component must appear in pairs.
+
+```html
+
+ ...
+
+```
+
+#### Global scene (pinia scene)
+
+```ts
+import { App } from 'ant-design-vue';
+import type { MessageInstance } from 'ant-design-vue/es/message/interface';
+import type { ModalStaticFunctions } from 'ant-design-vue/es/modal/confirm';
+import type { NotificationInstance } from 'ant-design-vue/es/notification/interface';
+
+export const useGloablStore = defineStore('global', () => {
+ const message: MessageInstance = ref();
+ const notification: NotificationInstance = ref();
+ const modal: Omit = ref();
+ (() => {
+ const staticFunction = App.useApp();
+ message.value = staticFunction.message;
+ modal.value = staticFunction.modal;
+ notification.value = staticFunction.notification;
+ })();
+
+ return { message, notification, modal };
+});
+```
+
+```html
+// sub page
+
+
+ Open message
+
+
+
+
+```
diff --git a/components/app/index.tsx b/components/app/index.tsx
new file mode 100644
index 000000000..0ee269a94
--- /dev/null
+++ b/components/app/index.tsx
@@ -0,0 +1,83 @@
+import { defineComponent, computed } from 'vue';
+import type { App as TypeApp, Plugin } from 'vue';
+import { initDefaultProps } from '../_util/props-util';
+import classNames from '../_util/classNames';
+import { objectType } from '../_util/type';
+import useConfigInject from '../config-provider/hooks/useConfigInject';
+import useMessage from '../message/useMessage';
+import useModal from '../modal/useModal';
+import useNotification from '../notification/useNotification';
+import type { AppConfig } from './context';
+import {
+ useProvideAppConfigContext,
+ useInjectAppConfigContext,
+ useProvideAppContext,
+ useInjectAppContext,
+} from './context';
+import useStyle from './style';
+
+export const AppProps = () => {
+ return {
+ rootClassName: String,
+ message: objectType(),
+ notification: objectType(),
+ };
+};
+
+const useApp = () => {
+ return useInjectAppContext();
+};
+
+const App = defineComponent({
+ name: 'AApp',
+ props: initDefaultProps(AppProps(), {}),
+ setup(props, { slots }) {
+ const { prefixCls } = useConfigInject('app', props);
+ const [wrapSSR, hashId] = useStyle(prefixCls);
+ const customClassName = computed(() => {
+ return classNames(hashId.value, prefixCls.value, props.rootClassName);
+ });
+
+ const appConfig = useInjectAppConfigContext();
+ const mergedAppConfig = computed(() => ({
+ message: { ...appConfig.message, ...props.message },
+ notification: { ...appConfig.notification, ...props.notification },
+ }));
+ useProvideAppConfigContext(mergedAppConfig.value);
+
+ const [messageApi, messageContextHolder] = useMessage(mergedAppConfig.value.message);
+ const [notificationApi, notificationContextHolder] = useNotification(
+ mergedAppConfig.value.notification,
+ );
+ const [ModalApi, ModalContextHolder] = useModal();
+
+ const memoizedContextValue = computed(() => ({
+ message: messageApi,
+ notification: notificationApi,
+ modal: ModalApi,
+ }));
+ useProvideAppContext(memoizedContextValue.value);
+
+ return () => {
+ return wrapSSR(
+
+ {ModalContextHolder()}
+ {messageContextHolder()}
+ {notificationContextHolder()}
+ {slots.default?.()}
+
,
+ );
+ };
+ },
+});
+
+App.useApp = useApp;
+
+App.install = function (app: TypeApp) {
+ app.component(App.name, App);
+};
+
+export default App as typeof App &
+ Plugin & {
+ readonly useApp: typeof useApp;
+ };
diff --git a/components/app/index.zh-CN.md b/components/app/index.zh-CN.md
new file mode 100644
index 000000000..e362ee065
--- /dev/null
+++ b/components/app/index.zh-CN.md
@@ -0,0 +1,130 @@
+---
+category: Components
+subtitle: 包裹组件
+cols: 1
+type: 其它
+title: App
+cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
+coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original
+---
+
+新的包裹组件,提供重置样式和提供消费上下文的默认环境。
+
+## 何时使用
+
+- 提供可消费 provide/inject 的 `message.xxx`、`Modal.xxx`、`notification.xxx` 的静态方法,可以简化 useMessage 等方法需要手动植入 `contextHolder` 的问题。
+- 提供基于 `.ant-app` 的默认重置样式,解决原生元素没有 antd 规范样式的问题。
+
+## API
+
+### App
+
+| 参数 | 说明 | 类型 | 默认值 | 版本 |
+| --- | --- | --- | --- | --- |
+| message | App 内 Message 的全局配置 | [MessageConfig](/components/message-cn/#messageconfig) | - | 4.x |
+| notification | App 内 Notification 的全局配置 | [NotificationConfig](/components/notification-cn/#notificationconfig) | - | 4.x |
+
+## 如何使用
+
+### 基础用法
+
+App 组件通过 `provide/inject` 提供上下文方法调用,因而 useApp 需要作为子组件才能使用,我们推荐在应用中顶层包裹 App。
+
+```html
+/*myPage.vue*/
+
+
+ Open message
+ Open modal
+ Open notification
+
+
+
+
+```
+
+注意:App.useApp 必须在 App 之下方可使用。
+
+#### 内嵌使用场景(如无必要,尽量不做嵌套)
+
+```html
+
+
+ ...
+ ...
+
+
+```
+
+#### 与 ConfigProvider 先后顺序
+
+App 组件只能在 `ConfigProvider` 之下才能使用 Design Token, 如果需要使用其样式重置能力,则 ConfigProvider 与 App 组件必须成对出现。
+
+```html
+
+ ...
+
+```
+
+#### 全局场景 (pinia 场景)
+
+```ts
+import { App } from 'ant-design-vue';
+import type { MessageInstance } from 'ant-design-vue/es/message/interface';
+import type { ModalStaticFunctions } from 'ant-design-vue/es/modal/confirm';
+import type { NotificationInstance } from 'ant-design-vue/es/notification/interface';
+
+export const useGloablStore = defineStore('global', () => {
+ const message: MessageInstance = ref();
+ const notification: NotificationInstance = ref();
+ const modal: Omit = ref();
+ (() => {
+ const staticFunction = App.useApp();
+ message.value = staticFunction.message;
+ modal.value = staticFunction.modal;
+ notification.value = staticFunction.notification;
+ })();
+
+ return { message, notification, modal };
+});
+```
+
+```html
+// sub page
+
+
+ Open message
+
+
+
+
+```
diff --git a/components/app/style/index.ts b/components/app/style/index.ts
new file mode 100644
index 000000000..031b0ffd0
--- /dev/null
+++ b/components/app/style/index.ts
@@ -0,0 +1,22 @@
+import type { FullToken, GenerateStyle } from '../../theme/internal';
+import { genComponentStyleHook } from '../../theme/internal';
+
+export type ComponentToken = {};
+
+interface AppToken extends FullToken<'App'> {}
+
+// =============================== Base ===============================
+const genBaseStyle: GenerateStyle = token => {
+ const { componentCls, colorText, fontSize, lineHeight, fontFamily } = token;
+ return {
+ [componentCls]: {
+ color: colorText,
+ fontSize,
+ lineHeight,
+ fontFamily,
+ },
+ };
+};
+
+// ============================== Export ==============================
+export default genComponentStyleHook('App', token => [genBaseStyle(token)]);
diff --git a/components/components.ts b/components/components.ts
index 7b741d1c6..2118711be 100644
--- a/components/components.ts
+++ b/components/components.ts
@@ -261,3 +261,6 @@ export { default as QRCode } from './qrcode';
export type { TourProps, TourStepProps } from './tour';
export { default as Tour } from './tour';
+
+export type { AppProps } from './app';
+export { default as App } from './app';
diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts
index 92f2fffb8..329cba4fd 100644
--- a/components/theme/interface/components.ts
+++ b/components/theme/interface/components.ts
@@ -46,7 +46,7 @@ import type { ComponentToken as TypographyComponentToken } from '../../typograph
import type { ComponentToken as UploadComponentToken } from '../../upload/style';
import type { ComponentToken as TourComponentToken } from '../../tour/style';
import type { ComponentToken as QRCodeComponentToken } from '../../qrcode/style';
-// import type { ComponentToken as AppComponentToken } from '../../app/style';
+import type { ComponentToken as AppComponentToken } from '../../app/style';
import type { ComponentToken as WaveToken } from '../../_util/wave/style';
export interface ComponentTokenMap {
@@ -112,7 +112,7 @@ export interface ComponentTokenMap {
Progress?: ProgressComponentToken;
Tour?: TourComponentToken;
QRCode?: QRCodeComponentToken;
- // App?: AppComponentToken;
+ App?: AppComponentToken;
// /** @private Internal TS definition. Do not use. */
Wave?: WaveToken;