diff --git a/packages/devextreme/js/__internal/grids/new/card_view/__snapshots__/widget.test.ts.snap b/packages/devextreme/js/__internal/grids/new/card_view/__snapshots__/widget.test.ts.snap
new file mode 100644
index 000000000000..b8a3b92dc104
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/card_view/__snapshots__/widget.test.ts.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`common initial render should be successfull 1`] = `
+
+ This is cardView
+
+`;
diff --git a/packages/devextreme/js/__internal/grids/new/card_view/main_view.tsx b/packages/devextreme/js/__internal/grids/new/card_view/main_view.tsx
new file mode 100644
index 000000000000..55b5b65437d2
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/card_view/main_view.tsx
@@ -0,0 +1,30 @@
+/* eslint-disable spellcheck/spell-checker */
+/* eslint-disable @typescript-eslint/explicit-member-accessibility */
+import { state } from '@ts/core/reactive/index';
+import { View } from '@ts/grids/new/grid_core/core/view';
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+interface MainViewProps {
+
+}
+
+// eslint-disable-next-line no-empty-pattern
+function MainViewComponent({
+
+}: MainViewProps): JSX.Element {
+ return (<>
+ This is cardView
+ >);
+}
+
+export class MainView extends View {
+ protected override component = MainViewComponent;
+
+ public static dependencies = [] as const;
+
+ // eslint-disable-next-line max-len
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type
+ protected override getProps() {
+ return state({});
+ }
+}
diff --git a/packages/devextreme/js/__internal/grids/new/card_view/options.ts b/packages/devextreme/js/__internal/grids/new/card_view/options.ts
new file mode 100644
index 000000000000..2448e49792c3
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/card_view/options.ts
@@ -0,0 +1,11 @@
+import * as GridCore from '@ts/grids/new/grid_core/options';
+
+/**
+ * @interface
+ */
+export type Options =
+ & GridCore.Options;
+
+export const defaultOptions = {
+ ...GridCore.defaultOptions,
+} satisfies Options;
diff --git a/packages/devextreme/js/__internal/grids/new/card_view/widget.test.ts b/packages/devextreme/js/__internal/grids/new/card_view/widget.test.ts
new file mode 100644
index 000000000000..baede2d8bbc0
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/card_view/widget.test.ts
@@ -0,0 +1,15 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { describe, expect, it } from '@jest/globals';
+
+import { CardView } from './widget';
+
+describe('common', () => {
+ describe('initial render', () => {
+ it('should be successfull', () => {
+ const container = document.createElement('div');
+ const cardView = new CardView(container, {});
+
+ expect(container).toMatchSnapshot();
+ });
+ });
+});
diff --git a/packages/devextreme/js/__internal/grids/new/card_view/widget.ts b/packages/devextreme/js/__internal/grids/new/card_view/widget.ts
new file mode 100644
index 000000000000..f444199a2015
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/card_view/widget.ts
@@ -0,0 +1,42 @@
+/* eslint-disable max-classes-per-file */
+/* eslint-disable spellcheck/spell-checker */
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
+import registerComponent from '@js/core/component_registrator';
+import $ from '@js/core/renderer';
+import { MainView as MainViewBase } from '@ts/grids/new/grid_core/main_view';
+import { GridCoreNew } from '@ts/grids/new/grid_core/widget';
+
+import { MainView } from './main_view';
+import { defaultOptions } from './options';
+
+export class CardViewBase extends GridCoreNew {
+ protected _registerDIContext(): void {
+ super._registerDIContext();
+ this.diContext.register(MainViewBase, MainView);
+ }
+
+ protected _initMarkup(): void {
+ super._initMarkup();
+ $(this.$element()).addClass('dx-cardview');
+ }
+
+ protected _initDIContext(): void {
+ super._initDIContext();
+ }
+
+ // eslint-disable-next-line max-len
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
+ protected _getDefaultOptions() {
+ return {
+ ...super._getDefaultOptions(),
+ ...defaultOptions,
+ };
+ }
+}
+
+export class CardView extends CardViewBase {}
+
+// @ts-expect-error
+registerComponent('dxCardView', CardView);
+
+export default CardView;
diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/core/view.tsx b/packages/devextreme/js/__internal/grids/new/grid_core/core/view.tsx
new file mode 100644
index 000000000000..497d30813c43
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/grid_core/core/view.tsx
@@ -0,0 +1,64 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
+/* eslint-disable @typescript-eslint/no-this-alias */
+/* eslint-disable @typescript-eslint/no-use-before-define */
+/* eslint-disable @typescript-eslint/no-unsafe-return */
+/* eslint-disable @typescript-eslint/ban-types */
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable max-classes-per-file */
+/* eslint-disable spellcheck/spell-checker */
+import type { Subscribable, Subscription } from '@ts/core/reactive/index';
+import { toSubscribable } from '@ts/core/reactive/index';
+import { Component, type ComponentType, render } from 'inferno';
+
+export abstract class View {
+ private inferno: undefined | ComponentType;
+
+ protected abstract component: ComponentType;
+
+ protected abstract getProps(): Subscribable;
+
+ public render(root: Element): Subscription {
+ const ViewComponent = this.component;
+ return toSubscribable(this.getProps()).subscribe((props: T) => {
+ // @ts-expect-error
+ render(, root);
+ });
+ }
+
+ public asInferno(): ComponentType {
+ // @ts-expect-error fixed in inferno v8
+ // eslint-disable-next-line no-return-assign
+ return this.inferno ??= this._asInferno();
+ }
+
+ private _asInferno() {
+ const view = this;
+
+ interface State {
+ props: T;
+ }
+
+ return class InfernoView extends Component<{}, State> {
+ private readonly subscription: Subscription;
+
+ constructor() {
+ super();
+ this.subscription = toSubscribable(view.getProps()).subscribe((props) => {
+ this.state ??= {
+ props,
+ };
+
+ if (this.state.props !== props) {
+ this.setState({ props });
+ }
+ });
+ }
+
+ public render(): JSX.Element | undefined {
+ const ViewComponent = view.component;
+ // @ts-expect-error
+ return ;
+ }
+ };
+ }
+}
diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/main_view.tsx b/packages/devextreme/js/__internal/grids/new/grid_core/main_view.tsx
new file mode 100644
index 000000000000..bd1c6ba4d326
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/grid_core/main_view.tsx
@@ -0,0 +1,7 @@
+/* eslint-disable @typescript-eslint/ban-types */
+/* eslint-disable spellcheck/spell-checker */
+/* eslint-disable @typescript-eslint/explicit-member-accessibility */
+
+import { View } from './core/view';
+
+export abstract class MainView extends View {}
diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/options.ts b/packages/devextreme/js/__internal/grids/new/grid_core/options.ts
new file mode 100644
index 000000000000..7020661d76d5
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/grid_core/options.ts
@@ -0,0 +1,54 @@
+import browser from '@js/core/utils/browser';
+import { isMaterialBased } from '@js/ui/themes';
+import type { WidgetOptions } from '@js/ui/widget/ui.widget';
+
+import type { GridCoreNew } from './widget';
+
+/**
+ * @interface
+ */
+export type Options =
+ & WidgetOptions;
+
+export const defaultOptions = {
+} satisfies Options;
+
+// TODO: separate by modules
+// TODO: add typing for defaultOptionRules
+export const defaultOptionsRules = [
+ {
+ device(): boolean {
+ // @ts-expect-error
+ return isMaterialBased();
+ },
+ options: {
+ headerFilter: {
+ height: 315,
+ },
+ editing: {
+ useIcons: true,
+ },
+ selection: {
+ showCheckBoxesMode: 'always',
+ },
+ },
+ },
+ {
+ device(): boolean | undefined {
+ return browser.webkit;
+ },
+ options: {
+ loadingTimeout: 30, // T344031
+ loadPanel: {
+ animation: {
+ show: {
+ easing: 'cubic-bezier(1, 0, 1, 0)',
+ duration: 500,
+ from: { opacity: 0 },
+ to: { opacity: 1 },
+ },
+ },
+ },
+ },
+ },
+];
diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/widget.ts b/packages/devextreme/js/__internal/grids/new/grid_core/widget.ts
new file mode 100644
index 000000000000..3c1a6fd2d661
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/new/grid_core/widget.ts
@@ -0,0 +1,68 @@
+/* eslint-disable @typescript-eslint/ban-types */
+/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
+/* eslint-disable spellcheck/spell-checker */
+// eslint-disable-next-line max-classes-per-file
+import Widget from '@js/ui/widget/ui.widget';
+import { DIContext } from '@ts/core/di/index';
+import type { Subscription } from '@ts/core/reactive/index';
+import { render } from 'inferno';
+
+import { MainView } from './main_view';
+import { defaultOptions, defaultOptionsRules, type Options } from './options';
+
+export class GridCoreNewBase<
+ TProperties extends Options = Options,
+> extends Widget {
+ protected renderSubscription?: Subscription;
+
+ protected diContext!: DIContext;
+
+ protected _registerDIContext(): void {
+ this.diContext = new DIContext();
+ }
+
+ protected _initDIContext(): void {
+ }
+
+ protected _init(): void {
+ // @ts-expect-error
+ super._init();
+ this._registerDIContext();
+ this._initDIContext();
+ }
+
+ // eslint-disable-next-line max-len
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
+ protected _getDefaultOptions() {
+ return {
+ // @ts-expect-error
+ ...super._getDefaultOptions() as {},
+ ...defaultOptions,
+ };
+ }
+
+ protected _defaultOptionsRules() {
+ // @ts-expect-error
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return super._defaultOptionsRules().concat(defaultOptionsRules);
+ }
+
+ protected _initMarkup(): void {
+ // @ts-expect-error
+ super._initMarkup();
+ // @ts-expect-error
+ this.renderSubscription = this.diContext.get(MainView).render(this.$element().get(0));
+ }
+
+ protected _clean(): void {
+ this.renderSubscription?.unsubscribe();
+ // @ts-expect-error
+ render(null, this.$element().get(0));
+ // @ts-expect-error
+ super._clean();
+ }
+}
+
+export class GridCoreNew extends GridCoreNewBase {}
diff --git a/packages/devextreme/js/bundles/modules/parts/widgets-web.js b/packages/devextreme/js/bundles/modules/parts/widgets-web.js
index e8d6d70fd681..959a240fde2a 100644
--- a/packages/devextreme/js/bundles/modules/parts/widgets-web.js
+++ b/packages/devextreme/js/bundles/modules/parts/widgets-web.js
@@ -9,6 +9,7 @@ ui.dxAccordion = require('../../../ui/accordion');
ui.dxContextMenu = require('../../../ui/context_menu');
ui.dxDataGrid = require('../../../ui/data_grid');
ui.dxTreeList = require('../../../ui/tree_list');
+ui.dxCardView = require('../../../ui/card_view');
ui.dxMenu = require('../../../ui/menu');
ui.dxPivotGrid = require('../../../ui/pivot_grid');
ui.dxPivotGridFieldChooser = require('../../../ui/pivot_grid_field_chooser');
diff --git a/packages/devextreme/js/ui/card_view.js b/packages/devextreme/js/ui/card_view.js
new file mode 100644
index 000000000000..663a6aca8521
--- /dev/null
+++ b/packages/devextreme/js/ui/card_view.js
@@ -0,0 +1 @@
+export { default } from '../__internal/grids/new/card_view/widget';