diff --git a/src/constants/widget-data.ts b/src/constants/widget-data.ts
index 3fbbe560..2dddbe2b 100644
--- a/src/constants/widget-data.ts
+++ b/src/constants/widget-data.ts
@@ -5,6 +5,7 @@ export const SeriesType = {
     Line: 'line',
     Pie: 'pie',
     Scatter: 'scatter',
+    Treemap: 'treemap',
 } as const;
 
 export enum DashStyle {
@@ -35,3 +36,11 @@ export enum LineCap {
     Square = 'square',
     None = 'none',
 }
+
+export enum LayoutAlgorithm {
+    Binary = 'binary',
+    Dice = 'dice',
+    Slice = 'slice',
+    SliceDice = 'slice-dice',
+    Squarify = 'squarify',
+}
diff --git a/src/i18n/keysets/en.json b/src/i18n/keysets/en.json
index 34c273c2..eaf2cea9 100644
--- a/src/i18n/keysets/en.json
+++ b/src/i18n/keysets/en.json
@@ -34,7 +34,9 @@
     "label_invalid-axis-linear-data-point": "It seems you are trying to use inappropriate data type for \"{{key}}\" value in series \"{{seriesName}}\" for axis with type \"linear\". Numbers and nulls are allowed.",
     "label_invalid-pie-data-value": "It seems you are trying to use inappropriate data type for \"value\" value. Only numbers are allowed.",
     "label_invalid-series-type": "It seems you haven't defined \"series.type\" property, or defined it incorrectly. Available values: [{{types}}].",
-    "label_invalid-series-property": "It seems you are trying to use inappropriate value for \"{{key}}\", or defined it incorrectly. Available values: [{{values}}]."
+    "label_invalid-series-property": "It seems you are trying to use inappropriate value for \"{{key}}\", or defined it incorrectly. Available values: [{{values}}].",
+    "label_invalid-treemap-redundant-value": "It seems you are trying to set \"value\" for container node. Check node with this properties: { id: \"{{id}}\", name: \"{{name}}\" }",
+    "label_invalid-treemap-missing-value": "It seems you are trying to use node without \"value\". Check node with this properties: { id: \"{{id}}\", name: \"{{name}}\" }"
   },
   "highcharts": {
     "reset-zoom-title": "Reset zoom",
diff --git a/src/i18n/keysets/ru.json b/src/i18n/keysets/ru.json
index b0e4e6f5..fa6ce219 100644
--- a/src/i18n/keysets/ru.json
+++ b/src/i18n/keysets/ru.json
@@ -36,7 +36,9 @@
     "label_invalid-axis-linear-data-point": "Похоже, что вы пытаетесь использовать недопустимый тип данных для значения \"{{key}}\" в серии \"{{seriesName}}\" для оси с типом \"linear\". Допускается использование чисел и значений null.",
     "label_invalid-pie-data-value": "Похоже, что вы пытаетесь использовать недопустимый тип данных для значения \"value\". Допускается только использование чисел.",
     "label_invalid-series-type": "Похоже, что вы не указали значение \"series.type\" или указали его неверно. Доступные значения: [{{types}}].",
-    "label_invalid-series-property": "Похоже, что вы пытаетесь использовать недопустимое значение для \"{{key}}\", или указали его неверно. Доступные значения: [{{values}}]."
+    "label_invalid-series-property": "Похоже, что вы пытаетесь использовать недопустимое значение для \"{{key}}\", или указали его неверно. Доступные значения: [{{values}}].",
+    "label_invalid-treemap-redundant-value": "Похоже, что вы пытаетесь установить значение \"value\" для узла, используемого в качестве контейнера. Проверьте узел с этими свойствами: { id: \"{{id}}\", name: \"{{name}}\" }",
+    "label_invalid-treemap-missing-value": "Похоже, что вы пытаетесь использовать узел без значения \"value\". Проверьте узел с этими свойствами: { id: \"{{id}}\", name: \"{{name}}\" }"
   },
   "highcharts": {
     "reset-zoom-title": "Сбросить увеличение",
diff --git a/src/plugins/d3/__stories__/treemap/Playground.stories.tsx b/src/plugins/d3/__stories__/treemap/Playground.stories.tsx
new file mode 100644
index 00000000..ca332ad8
--- /dev/null
+++ b/src/plugins/d3/__stories__/treemap/Playground.stories.tsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import {StoryObj} from '@storybook/react';
+import {Button} from '@gravity-ui/uikit';
+import {settings} from '../../../../libs';
+import {ChartKit} from '../../../../components/ChartKit';
+import type {ChartKitRef} from '../../../../types';
+import type {ChartKitWidgetData} from '../../../../types/widget-data';
+import {D3Plugin} from '../..';
+
+const prepareData = (): ChartKitWidgetData => {
+    return {
+        series: {
+            data: [
+                {
+                    type: 'treemap',
+                    name: 'Example',
+                    dataLabels: {
+                        enabled: true,
+                    },
+                    layoutAlgorithm: 'binary',
+                    levels: [{index: 1}, {index: 2}, {index: 3}],
+                    data: [
+                        {name: 'One', value: 15},
+                        {name: 'Two', value: 10},
+                        {name: 'Three', value: 15},
+                        {name: 'Four'},
+                        {name: 'Four-1', value: 5, parentId: 'Four'},
+                        {name: 'Four-2', parentId: 'Four'},
+                        {name: 'Four-3', value: 4, parentId: 'Four'},
+                        {name: 'Four-2-1', value: 5, parentId: 'Four-2'},
+                        {name: 'Four-2-2', value: 7, parentId: 'Four-2'},
+                        {name: 'Four-2-3', value: 10, parentId: 'Four-2'},
+                    ],
+                },
+            ],
+        },
+    };
+};
+
+const ChartStory = ({data}: {data: ChartKitWidgetData}) => {
+    const [shown, setShown] = React.useState(false);
+    const chartkitRef = React.useRef<ChartKitRef>();
+
+    if (!shown) {
+        settings.set({plugins: [D3Plugin]});
+        return <Button onClick={() => setShown(true)}>Show chart</Button>;
+    }
+
+    return (
+        <div style={{height: '300px', width: '100%'}}>
+            <ChartKit ref={chartkitRef} type="d3" data={data} />
+        </div>
+    );
+};
+
+export const TreemapPlayground: StoryObj<typeof ChartStory> = {
+    name: 'Playground',
+    args: {data: prepareData()},
+    argTypes: {
+        data: {
+            control: 'object',
+        },
+    },
+};
+
+export default {
+    title: 'Plugins/D3/Treemap',
+    component: ChartStory,
+};
diff --git a/src/plugins/d3/renderer/components/Chart.tsx b/src/plugins/d3/renderer/components/Chart.tsx
index 9959ab54..555b7579 100644
--- a/src/plugins/d3/renderer/components/Chart.tsx
+++ b/src/plugins/d3/renderer/components/Chart.tsx
@@ -32,7 +32,6 @@ type Props = {
 };
 
 export const Chart = (props: Props) => {
-    // FIXME: add data validation
     const {width, height, data} = props;
     const svgRef = React.useRef<SVGSVGElement>(null);
     const dispatcher = React.useMemo(() => {
@@ -45,7 +44,6 @@ export const Chart = (props: Props) => {
         () => getPreparedXAxis({xAxis: data.xAxis, width, series: data.series.data}),
         [data, width],
     );
-
     const yAxis = React.useMemo(
         () => getPreparedYAxis({series: data.series.data, yAxis: data.yAxis}),
         [data, width],
diff --git a/src/plugins/d3/renderer/components/Tooltip/DefaultContent.tsx b/src/plugins/d3/renderer/components/Tooltip/DefaultContent.tsx
index 259816b5..8817aacd 100644
--- a/src/plugins/d3/renderer/components/Tooltip/DefaultContent.tsx
+++ b/src/plugins/d3/renderer/components/Tooltip/DefaultContent.tsx
@@ -1,7 +1,11 @@
 import React from 'react';
 import get from 'lodash/get';
 import {dateTime} from '@gravity-ui/date-utils';
-import type {ChartKitWidgetSeriesData, TooltipDataChunk} from '../../../../../types';
+import type {
+    ChartKitWidgetSeriesData,
+    TooltipDataChunk,
+    TreemapSeriesData,
+} from '../../../../../types';
 import {formatNumber} from '../../../../shared';
 import type {PreparedAxis, PreparedPieSeries} from '../../hooks';
 import {getDataCategoryValue} from '../../utils';
@@ -81,8 +85,9 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => {
                             </div>
                         );
                     }
-                    case 'pie': {
-                        const pieSeriesData = data as PreparedPieSeries;
+                    case 'pie':
+                    case 'treemap': {
+                        const pieSeriesData = data as PreparedPieSeries | TreemapSeriesData;
 
                         return (
                             <div key={id}>
diff --git a/src/plugins/d3/renderer/constants/defaults/series-options.ts b/src/plugins/d3/renderer/constants/defaults/series-options.ts
index e1d7b907..5069929c 100644
--- a/src/plugins/d3/renderer/constants/defaults/series-options.ts
+++ b/src/plugins/d3/renderer/constants/defaults/series-options.ts
@@ -91,4 +91,16 @@ export const seriesOptionsDefaults: SeriesOptionsDefaults = {
             },
         },
     },
+    treemap: {
+        states: {
+            hover: {
+                enabled: true,
+                brightness: 0.3,
+            },
+            inactive: {
+                enabled: false,
+                opacity: 0.5,
+            },
+        },
+    },
 };
diff --git a/src/plugins/d3/renderer/hooks/useSeries/index.ts b/src/plugins/d3/renderer/hooks/useSeries/index.ts
index dbfbd9c9..06443317 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/index.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/index.ts
@@ -62,9 +62,7 @@ export const useSeries = (args: Args) => {
         getActiveLegendItems(preparedSeries),
     );
     const chartSeries = React.useMemo<PreparedSeries[]>(() => {
-        return preparedSeries.map((singleSeries, i) => {
-            singleSeries.id = `Series ${i + 1}`;
-
+        return preparedSeries.map((singleSeries) => {
             if (singleSeries.legend.enabled) {
                 return {
                     ...singleSeries,
@@ -88,6 +86,7 @@ export const useSeries = (args: Args) => {
 
     const handleLegendItemClick: OnLegendItemClick = React.useCallback(
         ({name, metaKey}) => {
+            const allItems = getAllLegendItems(preparedSeries);
             const onlyItemSelected =
                 activeLegendItems.length === 1 && activeLegendItems.includes(name);
             let nextActiveLegendItems: string[];
@@ -96,8 +95,10 @@ export const useSeries = (args: Args) => {
                 nextActiveLegendItems = activeLegendItems.filter((item) => item !== name);
             } else if (metaKey && !activeLegendItems.includes(name)) {
                 nextActiveLegendItems = activeLegendItems.concat(name);
+            } else if (onlyItemSelected && allItems.length === 1) {
+                nextActiveLegendItems = [];
             } else if (onlyItemSelected) {
-                nextActiveLegendItems = getAllLegendItems(preparedSeries);
+                nextActiveLegendItems = allItems;
             } else {
                 nextActiveLegendItems = [name];
             }
diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepare-treemap.ts b/src/plugins/d3/renderer/hooks/useSeries/prepare-treemap.ts
new file mode 100644
index 00000000..1716b0c8
--- /dev/null
+++ b/src/plugins/d3/renderer/hooks/useSeries/prepare-treemap.ts
@@ -0,0 +1,48 @@
+import type {ScaleOrdinal} from 'd3';
+import get from 'lodash/get';
+
+import {LayoutAlgorithm} from '../../../../../constants';
+import type {ChartKitWidgetSeriesOptions, TreemapSeries} from '../../../../../types';
+import {getRandomCKId} from '../../../../../utils';
+
+import {DEFAULT_DATALABELS_PADDING, DEFAULT_DATALABELS_STYLE} from './constants';
+import type {PreparedLegend, PreparedTreemapSeries} from './types';
+import {prepareLegendSymbol} from './utils';
+
+type PrepareTreemapSeriesArgs = {
+    colorScale: ScaleOrdinal<string, string>;
+    legend: PreparedLegend;
+    series: TreemapSeries[];
+    seriesOptions?: ChartKitWidgetSeriesOptions;
+};
+
+export function prepareTreemap(args: PrepareTreemapSeriesArgs) {
+    const {colorScale, legend, series} = args;
+
+    return series.map<PreparedTreemapSeries>((s) => {
+        const id = getRandomCKId();
+        const name = s.name || '';
+        const color = s.color || colorScale(name);
+
+        return {
+            color,
+            data: s.data,
+            dataLabels: {
+                enabled: get(s, 'dataLabels.enabled', true),
+                style: Object.assign({}, DEFAULT_DATALABELS_STYLE, s.dataLabels?.style),
+                padding: get(s, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
+                allowOverlap: get(s, 'dataLabels.allowOverlap', false),
+            },
+            id,
+            type: s.type,
+            name,
+            visible: get(s, 'visible', true),
+            legend: {
+                enabled: get(s, 'legend.enabled', legend.enabled),
+                symbol: prepareLegendSymbol(s),
+            },
+            levels: s.levels,
+            layoutAlgorithm: get(s, 'layoutAlgorithm', LayoutAlgorithm.Binary),
+        };
+    });
+}
diff --git a/src/plugins/d3/renderer/hooks/useSeries/prepareSeries.ts b/src/plugins/d3/renderer/hooks/useSeries/prepareSeries.ts
index 87b26ce4..ba8fd13f 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/prepareSeries.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/prepareSeries.ts
@@ -9,16 +9,18 @@ import type {
     LineSeries,
     PieSeries,
     ScatterSeries,
+    TreemapSeries,
 } from '../../../../../types';
+import {ChartKitError} from '../../../../../libs';
 
 import type {PreparedLegend, PreparedSeries} from './types';
 import {prepareLineSeries} from './prepare-line';
 import {prepareBarXSeries} from './prepare-bar-x';
 import {prepareBarYSeries} from './prepare-bar-y';
-import {ChartKitError} from '../../../../../libs';
 import {preparePieSeries} from './prepare-pie';
 import {prepareArea} from './prepare-area';
 import {prepareScatterSeries} from './prepare-scatter';
+import {prepareTreemap} from './prepare-treemap';
 
 export function prepareSeries(args: {
     type: ChartKitWidgetSeries['type'];
@@ -63,6 +65,14 @@ export function prepareSeries(args: {
                 colorScale,
             });
         }
+        case 'treemap': {
+            return prepareTreemap({
+                series: series as TreemapSeries[],
+                seriesOptions,
+                legend,
+                colorScale,
+            });
+        }
         default: {
             throw new ChartKitError({
                 message: `Series type "${type}" does not support data preparation for series that do not support the presence of axes`,
diff --git a/src/plugins/d3/renderer/hooks/useSeries/types.ts b/src/plugins/d3/renderer/hooks/useSeries/types.ts
index 3500f467..8d95983a 100644
--- a/src/plugins/d3/renderer/hooks/useSeries/types.ts
+++ b/src/plugins/d3/renderer/hooks/useSeries/types.ts
@@ -18,9 +18,11 @@ import {
     SymbolLegendSymbolOptions,
     AreaSeries,
     AreaSeriesData,
+    TreemapSeries,
+    TreemapSeriesData,
 } from '../../../../../types';
 import type {SeriesOptionsDefaults} from '../../constants';
-import {DashStyle, LineCap, SymbolType} from '../../../../../constants';
+import {DashStyle, LayoutAlgorithm, LineCap, SymbolType} from '../../../../../constants';
 
 export type RectLegendSymbol = {
     shape: 'rect';
@@ -228,13 +230,27 @@ export type PreparedAreaSeries = {
     };
 } & BasePreparedSeries;
 
+export type PreparedTreemapSeries = {
+    type: TreemapSeries['type'];
+    data: TreemapSeriesData[];
+    dataLabels: {
+        enabled: boolean;
+        style: BaseTextStyle;
+        padding: number;
+        allowOverlap: boolean;
+    };
+    layoutAlgorithm: `${LayoutAlgorithm}`;
+} & BasePreparedSeries &
+    TreemapSeries;
+
 export type PreparedSeries =
     | PreparedScatterSeries
     | PreparedBarXSeries
     | PreparedBarYSeries
     | PreparedPieSeries
     | PreparedLineSeries
-    | PreparedAreaSeries;
+    | PreparedAreaSeries
+    | PreparedTreemapSeries;
 
 export type PreparedSeriesOptions = SeriesOptionsDefaults;
 
diff --git a/src/plugins/d3/renderer/hooks/useShapes/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/index.tsx
index ee9204b0..f6f0f57d 100644
--- a/src/plugins/d3/renderer/hooks/useShapes/index.tsx
+++ b/src/plugins/d3/renderer/hooks/useShapes/index.tsx
@@ -11,6 +11,7 @@ import type {
     PreparedLineSeries,
     PreparedPieSeries,
     PreparedScatterSeries,
+    PreparedTreemapSeries,
     PreparedSeries,
     PreparedSeriesOptions,
 } from '../';
@@ -31,6 +32,8 @@ export type {PreparedScatterData} from './scatter/types';
 import {AreaSeriesShapes} from './area';
 import {prepareAreaData} from './area/prepare-data';
 import type {PreparedAreaData} from './area/types';
+import {TreemapSeriesShape} from './treemap';
+import {prepareTreemapData} from './treemap/prepare-data';
 
 import './styles.scss';
 
@@ -189,7 +192,6 @@ export const useShapes = (args: Args) => {
                         boundsWidth,
                         boundsHeight,
                     });
-
                     acc.push(
                         <PieSeriesShapes
                             key="pie"
@@ -199,6 +201,25 @@ export const useShapes = (args: Args) => {
                             svgContainer={svgContainer}
                         />,
                     );
+                    break;
+                }
+                case 'treemap': {
+                    const preparedData = prepareTreemapData({
+                        // We should have exactly one series with "treemap" type
+                        // Otherwise data validation should emit an error
+                        series: chartSeries[0] as PreparedTreemapSeries,
+                        width: boundsWidth,
+                        height: boundsHeight,
+                    });
+                    acc.push(
+                        <TreemapSeriesShape
+                            key="treemap"
+                            dispatcher={dispatcher}
+                            preparedData={preparedData}
+                            seriesOptions={seriesOptions}
+                            svgContainer={svgContainer}
+                        />,
+                    );
                 }
             }
             return acc;
diff --git a/src/plugins/d3/renderer/hooks/useShapes/styles.scss b/src/plugins/d3/renderer/hooks/useShapes/styles.scss
index d4c264b1..6a81d03c 100644
--- a/src/plugins/d3/renderer/hooks/useShapes/styles.scss
+++ b/src/plugins/d3/renderer/hooks/useShapes/styles.scss
@@ -31,3 +31,12 @@
         alignment-baseline: after-edge;
     }
 }
+
+.chartkit-d3-treemap {
+    &__label {
+        fill: var(--g-color-text-complementary);
+        alignment-baseline: text-before-edge;
+        user-select: none;
+        pointer-events: none;
+    }
+}
diff --git a/src/plugins/d3/renderer/hooks/useShapes/treemap/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/treemap/index.tsx
new file mode 100644
index 00000000..b7482c4e
--- /dev/null
+++ b/src/plugins/d3/renderer/hooks/useShapes/treemap/index.tsx
@@ -0,0 +1,137 @@
+import React from 'react';
+import {color, pointer, select} from 'd3';
+import type {BaseType, Dispatch, HierarchyRectangularNode} from 'd3';
+import get from 'lodash/get';
+
+import type {TooltipDataChunkTreemap, TreemapSeriesData} from '../../../../../../types';
+import {setEllipsisForOverflowTexts} from '../../../utils';
+import {block} from '../../../../../../utils/cn';
+
+import {PreparedSeriesOptions} from '../../useSeries/types';
+import type {PreparedTreemapData, TreemapLabelData} from './types';
+
+const b = block('d3-treemap');
+
+type ShapeProps = {
+    dispatcher: Dispatch<object>;
+    preparedData: PreparedTreemapData;
+    seriesOptions: PreparedSeriesOptions;
+    svgContainer: SVGSVGElement | null;
+};
+
+export const TreemapSeriesShape = (props: ShapeProps) => {
+    const {dispatcher, preparedData, seriesOptions, svgContainer} = props;
+    const ref = React.useRef<SVGGElement>(null);
+
+    React.useEffect(() => {
+        if (!ref.current) {
+            return () => {};
+        }
+
+        const svgElement = select(ref.current);
+        svgElement.selectAll('*').remove();
+        const {labelData, leaves, series} = preparedData;
+        const leaf = svgElement
+            .selectAll('g')
+            .data(leaves)
+            .join('g')
+            .attr('transform', (d) => `translate(${d.x0},${d.y0})`);
+        const rectSelection = leaf
+            .append('rect')
+            .attr('id', (d) => d.id || d.name)
+            .attr('fill', (d) => {
+                if (d.data.color) {
+                    return d.data.color;
+                }
+
+                const levelOptions = series.levels?.find((l) => l.index === d.depth);
+                return levelOptions?.color || series.color;
+            })
+            .attr('width', (d) => d.x1 - d.x0)
+            .attr('height', (d) => d.y1 - d.y0);
+        const labelSelection = svgElement
+            .selectAll<SVGTextElement, typeof labelData>('tspan')
+            .data(labelData)
+            .join('text')
+            .text((d) => d.text)
+            .attr('class', b('label'))
+            .attr('x', (d) => d.x)
+            .attr('y', (d) => d.y)
+            .style('font-size', () => series.dataLabels.style.fontSize)
+            .style('font-weight', () => series.dataLabels.style?.fontWeight || null)
+            .style('fill', () => series.dataLabels.style?.fontColor || null)
+            .call(setEllipsisForOverflowTexts, (d) => d.width);
+
+        const eventName = `hover-shape.pie`;
+        const hoverOptions = get(seriesOptions, 'treemap.states.hover');
+        const inactiveOptions = get(seriesOptions, 'treemap.states.inactive');
+        svgElement
+            .on('mousemove', (e) => {
+                const hoveredRect = select<BaseType, HierarchyRectangularNode<TreemapSeriesData>>(
+                    e.target,
+                );
+                const datum = hoveredRect.datum();
+                dispatcher.call(
+                    'hover-shape',
+                    {},
+                    [{data: datum.data, series}],
+                    pointer(e, svgContainer),
+                );
+            })
+            .on('mouseleave', () => {
+                dispatcher.call('hover-shape', {}, undefined);
+            });
+
+        dispatcher.on(eventName, (data?: TooltipDataChunkTreemap[]) => {
+            const hoverEnabled = hoverOptions?.enabled;
+            const inactiveEnabled = inactiveOptions?.enabled;
+            const hoveredData = data?.[0].data;
+            rectSelection.datum((d, index, list) => {
+                const currentRect = select<BaseType, HierarchyRectangularNode<TreemapSeriesData>>(
+                    list[index],
+                );
+                const hovered = Boolean(hoverEnabled && hoveredData === d.data);
+                const inactive = Boolean(inactiveEnabled && hoveredData && !hovered);
+                currentRect
+                    .attr('fill', (currentD) => {
+                        const levelOptions = series.levels?.find((l) => l.index === currentD.depth);
+                        const initialColor = levelOptions?.color || d.data.color || series.color;
+                        if (hovered) {
+                            return (
+                                color(initialColor)
+                                    ?.brighter(hoverOptions?.brightness)
+                                    .toString() || initialColor
+                            );
+                        }
+                        return initialColor;
+                    })
+                    .attr('opacity', () => {
+                        if (inactive) {
+                            return inactiveOptions?.opacity || null;
+                        }
+                        return null;
+                    });
+
+                return d;
+            });
+            labelSelection.datum((d, index, list) => {
+                const currentLabel = select<BaseType, TreemapLabelData>(list[index]);
+                const hovered = Boolean(hoverEnabled && hoveredData === d.nodeData);
+                const inactive = Boolean(inactiveEnabled && hoveredData && !hovered);
+                currentLabel.attr('opacity', () => {
+                    if (inactive) {
+                        return inactiveOptions?.opacity || null;
+                    }
+                    return null;
+                });
+                return d;
+            });
+        });
+
+        return () => {
+            dispatcher.on(eventName, null);
+        };
+    }, [dispatcher, preparedData, seriesOptions, svgContainer]);
+
+    return <g ref={ref} className={b()} />;
+};
diff --git a/src/plugins/d3/renderer/hooks/useShapes/treemap/prepare-data.ts b/src/plugins/d3/renderer/hooks/useShapes/treemap/prepare-data.ts
new file mode 100644
index 00000000..305948bf
--- /dev/null
+++ b/src/plugins/d3/renderer/hooks/useShapes/treemap/prepare-data.ts
@@ -0,0 +1,95 @@
+import {
+    stratify,
+    treemap,
+    treemapBinary,
+    treemapDice,
+    treemapSlice,
+    treemapSliceDice,
+    treemapSquarify,
+} from 'd3';
+import type {HierarchyRectangularNode} from 'd3';
+
+import {LayoutAlgorithm} from '../../../../../../constants';
+import type {TreemapSeriesData} from '../../../../../../types';
+
+import type {PreparedTreemapSeries} from '../../useSeries/types';
+import type {PreparedTreemapData, TreemapLabelData} from './types';
+
+const DEFAULT_PADDING = 1;
+
+function getLabelData(data: HierarchyRectangularNode<TreemapSeriesData>[]): TreemapLabelData[] {
+    return data.map((d) => {
+        const text = d.data.name;
+
+        return {
+            text,
+            x: d.x0,
+            y: d.y0,
+            width: d.x1 - d.x0,
+            nodeData: d.data,
+        };
+    });
+}
+
+export function prepareTreemapData(args: {
+    series: PreparedTreemapSeries;
+    width: number;
+    height: number;
+}): PreparedTreemapData {
+    const {series, width, height} = args;
+    const dataWithRootNode = getSeriesDataWithRootNode(series);
+    const hierarchy = stratify<TreemapSeriesData>()
+        .id((d) => d.id || d.name)
+        .parentId((d) => d.parentId)(dataWithRootNode)
+        .sum((d) => d.value || 0);
+    const treemapInstance = treemap<TreemapSeriesData>();
+
+    switch (series.layoutAlgorithm) {
+        case LayoutAlgorithm.Binary: {
+            treemapInstance.tile(treemapBinary);
+            break;
+        }
+        case LayoutAlgorithm.Dice: {
+            treemapInstance.tile(treemapDice);
+            break;
+        }
+        case LayoutAlgorithm.Slice: {
+            treemapInstance.tile(treemapSlice);
+            break;
+        }
+        case LayoutAlgorithm.SliceDice: {
+            treemapInstance.tile(treemapSliceDice);
+            break;
+        }
+        case LayoutAlgorithm.Squarify: {
+            treemapInstance.tile(treemapSquarify);
+            break;
+        }
+    }
+
+    const root = treemapInstance.size([width, height]).paddingInner((d) => {
+        const levelOptions = series.levels?.find((l) => l.index === d.depth + 1);
+        return levelOptions?.padding ?? DEFAULT_PADDING;
+    })(hierarchy);
+    const leaves = root.leaves();
+    const labelData: TreemapLabelData[] = series.dataLabels?.enabled ? getLabelData(leaves) : [];
+
+    return {labelData, leaves, series};
+}
+
+function getSeriesDataWithRootNode(series: PreparedTreemapSeries) {
+    return series.data.reduce<TreemapSeriesData[]>(
+        (acc, d) => {
+            const dataChunk = Object.assign({}, d);
+
+            if (!dataChunk.parentId) {
+                dataChunk.parentId = series.id;
+            }
+
+            acc.push(dataChunk);
+
+            return acc;
+        },
+        [{name: series.name, id: series.id}],
+    );
+}
diff --git a/src/plugins/d3/renderer/hooks/useShapes/treemap/types.ts b/src/plugins/d3/renderer/hooks/useShapes/treemap/types.ts
new file mode 100644
index 00000000..d5fab99d
--- /dev/null
+++ b/src/plugins/d3/renderer/hooks/useShapes/treemap/types.ts
@@ -0,0 +1,18 @@
+import type {HierarchyRectangularNode} from 'd3';
+
+import type {TreemapSeriesData} from '../../../../../../types';
+import type {PreparedTreemapSeries} from '../../useSeries/types';
+
+export type TreemapLabelData = {
+    text: string;
+    x: number;
+    y: number;
+    width: number;
+    nodeData: TreemapSeriesData;
+};
+
+export type PreparedTreemapData = {
+    labelData: TreemapLabelData[];
+    leaves: HierarchyRectangularNode<TreemapSeriesData<any>>[];
+    series: PreparedTreemapSeries;
+};
diff --git a/src/plugins/d3/renderer/utils/index.ts b/src/plugins/d3/renderer/utils/index.ts
index 2e2c506c..ba1dc62a 100644
--- a/src/plugins/d3/renderer/utils/index.ts
+++ b/src/plugins/d3/renderer/utils/index.ts
@@ -22,7 +22,7 @@ export * from './axis';
 export * from './labels';
 export * from './symbol';
 
-const CHARTS_WITHOUT_AXIS: ChartKitWidgetSeries['type'][] = ['pie'];
+const CHARTS_WITHOUT_AXIS: ChartKitWidgetSeries['type'][] = ['pie', 'treemap'];
 
 export type AxisDirection = 'x' | 'y';
 
diff --git a/src/plugins/d3/renderer/validation/__tests__/validation.test.ts b/src/plugins/d3/renderer/validation/__tests__/validation.test.ts
index 1c93ba19..f05d0a75 100644
--- a/src/plugins/d3/renderer/validation/__tests__/validation.test.ts
+++ b/src/plugins/d3/renderer/validation/__tests__/validation.test.ts
@@ -89,4 +89,45 @@ describe('plugins/d3/validation', () => {
             expect(error?.code).toEqual(CHARTKIT_ERROR_CODE.INVALID_DATA);
         },
     );
+
+    test.each([
+        [[{name: '1'} /* error */]],
+        [[{name: '1'}, {name: '2', parentId: '1'} /* error */]],
+        [
+            [
+                {name: '1', value: 1}, // error
+                {name: '2', parentId: '1', value: 1},
+            ],
+        ],
+        [
+            [
+                {name: '1'},
+                {name: '2', parentId: '1', value: 1}, // error
+                {name: '3', parentId: '2', value: 1},
+                {name: '4', parentId: '2', value: 1},
+            ],
+        ],
+    ])(
+        '[Treemap Series] validateData should throw an error in case of invalid data (data: %j)',
+        (data) => {
+            let error: ChartKitError | null = null;
+
+            try {
+                validateData({
+                    series: {
+                        data: [
+                            {
+                                type: 'treemap',
+                                data,
+                            },
+                        ] as ChartKitWidgetData['series']['data'],
+                    },
+                });
+            } catch (e) {
+                error = e as ChartKitError;
+            }
+
+            expect(error?.code).toEqual(CHARTKIT_ERROR_CODE.INVALID_DATA);
+        },
+    );
 });
diff --git a/src/plugins/d3/renderer/validation/index.ts b/src/plugins/d3/renderer/validation/index.ts
index f8d18df6..e7704a51 100644
--- a/src/plugins/d3/renderer/validation/index.ts
+++ b/src/plugins/d3/renderer/validation/index.ts
@@ -13,6 +13,7 @@ import {
     LineSeries,
     PieSeries,
     ScatterSeries,
+    TreemapSeries,
 } from '../../../../types';
 import {i18n} from '../../../../i18n';
 
@@ -137,6 +138,38 @@ const validateStacking = ({series}: {series: AreaSeries | BarXSeries | BarYSerie
     }
 };
 
+const validateTreemapSeries = ({series}: {series: TreemapSeries}) => {
+    const parentIds: Record<string, boolean> = {};
+    series.data.forEach((d) => {
+        if (d.parentId && !parentIds[d.parentId]) {
+            parentIds[d.parentId] = true;
+        }
+    });
+    series.data.forEach((d) => {
+        const idOrName = d.id || d.name;
+
+        if (parentIds[idOrName] && typeof d.value === 'number') {
+            throw new ChartKitError({
+                code: CHARTKIT_ERROR_CODE.INVALID_DATA,
+                message: i18n('error', 'label_invalid-treemap-redundant-value', {
+                    id: d.id,
+                    name: d.name,
+                }),
+            });
+        }
+
+        if (!parentIds[idOrName] && typeof d.value !== 'number') {
+            throw new ChartKitError({
+                code: CHARTKIT_ERROR_CODE.INVALID_DATA,
+                message: i18n('error', 'label_invalid-treemap-missing-value', {
+                    id: d.id,
+                    name: d.name,
+                }),
+            });
+        }
+    });
+};
+
 const validateSeries = (args: {
     series: ChartKitWidgetSeries;
     xAxis?: ChartKitWidgetAxis;
@@ -168,10 +201,30 @@ const validateSeries = (args: {
         }
         case 'pie': {
             validatePieSeries({series});
+            break;
+        }
+        case 'treemap': {
+            validateTreemapSeries({series});
         }
     }
 };
 
+const countSeriesByType = (args: {
+    series: ChartKitWidgetSeries[];
+    type: ChartKitWidgetSeries['type'];
+}) => {
+    const {series, type} = args;
+    let count = 0;
+
+    series.forEach((s) => {
+        if (s.type === type) {
+            count += 1;
+        }
+    });
+
+    return count;
+};
+
 export const validateData = (data?: ChartKitWidgetData) => {
     if (isEmpty(data) || isEmpty(data.series) || isEmpty(data.series.data)) {
         throw new ChartKitError({
@@ -187,6 +240,18 @@ export const validateData = (data?: ChartKitWidgetData) => {
         });
     }
 
+    const treemapSeriesCount = countSeriesByType({
+        series: data.series.data,
+        type: SeriesType.Treemap,
+    });
+
+    if (treemapSeriesCount > 1) {
+        throw new ChartKitError({
+            code: CHARTKIT_ERROR_CODE.INVALID_DATA,
+            message: 'It looks like you are trying to define more than one "treemap" series.',
+        });
+    }
+
     data.series.data.forEach((series) => {
         validateSeries({series, yAxis: data.yAxis?.[0], xAxis: data.xAxis});
     });
diff --git a/src/types/widget-data/index.ts b/src/types/widget-data/index.ts
index f4021510..031a3afa 100644
--- a/src/types/widget-data/index.ts
+++ b/src/types/widget-data/index.ts
@@ -19,6 +19,7 @@ export * from './series';
 export * from './title';
 export * from './tooltip';
 export * from './halo';
+export * from './treemap';
 
 export type ChartKitWidgetData<T = any> = {
     chart?: ChartKitWidgetChart;
diff --git a/src/types/widget-data/series.ts b/src/types/widget-data/series.ts
index 916fdf33..12eeb46d 100644
--- a/src/types/widget-data/series.ts
+++ b/src/types/widget-data/series.ts
@@ -6,6 +6,7 @@ import type {LineSeries, LineSeriesData} from './line';
 import type {BarYSeries, BarYSeriesData} from './bar-y';
 import type {PointMarkerOptions} from './marker';
 import type {AreaSeries, AreaSeriesData} from './area';
+import type {TreemapSeries, TreemapSeriesData} from './treemap';
 import type {Halo} from './halo';
 
 import {DashStyle, LineCap} from '../../constants';
@@ -16,7 +17,8 @@ export type ChartKitWidgetSeries<T = any> =
     | BarXSeries<T>
     | BarYSeries<T>
     | LineSeries<T>
-    | AreaSeries<T>;
+    | AreaSeries<T>
+    | TreemapSeries<T>;
 
 export type ChartKitWidgetSeriesData<T = any> =
     | ScatterSeriesData<T>
@@ -24,7 +26,8 @@ export type ChartKitWidgetSeriesData<T = any> =
     | BarXSeriesData<T>
     | BarYSeriesData<T>
     | LineSeriesData<T>
-    | AreaSeriesData<T>;
+    | AreaSeriesData<T>
+    | TreemapSeriesData<T>;
 
 export type DataLabelRendererData<T = any> = {
     data: ChartKitWidgetSeriesData<T>;
@@ -64,7 +67,6 @@ export type BasicInactiveState = {
 };
 
 export type ChartKitWidgetSeriesOptions = {
-    // todo
     /** Individual data label for each point. */
     dataLabels?: {
         /** Enable or disable the data labels */
@@ -216,4 +218,11 @@ export type ChartKitWidgetSeriesOptions = {
         /** Options for the point markers of line series */
         marker?: PointMarkerOptions;
     };
+    treemap?: {
+        /** Options for the series states that provide additional styling information to the series. */
+        states?: {
+            hover?: BasicHoverState;
+            inactive?: BasicInactiveState;
+        };
+    };
 };
diff --git a/src/types/widget-data/tooltip.ts b/src/types/widget-data/tooltip.ts
index cf07d92d..82457b08 100644
--- a/src/types/widget-data/tooltip.ts
+++ b/src/types/widget-data/tooltip.ts
@@ -4,6 +4,7 @@ import type {ScatterSeries, ScatterSeriesData} from './scatter';
 import type {LineSeries, LineSeriesData} from './line';
 import type {BarYSeries, BarYSeriesData} from './bar-y';
 import type {AreaSeries, AreaSeriesData} from './area';
+import type {TreemapSeries, TreemapSeriesData} from './treemap';
 
 export type TooltipDataChunkBarX<T = any> = {
     data: BarXSeriesData<T>;
@@ -51,13 +52,19 @@ export type TooltipDataChunkArea<T = any> = {
     };
 };
 
+export type TooltipDataChunkTreemap<T = any> = {
+    data: TreemapSeriesData<T>;
+    series: TreemapSeries<T>;
+};
+
 export type TooltipDataChunk<T = any> =
     | TooltipDataChunkBarX<T>
     | TooltipDataChunkBarY<T>
     | TooltipDataChunkPie<T>
     | TooltipDataChunkScatter<T>
     | TooltipDataChunkLine<T>
-    | TooltipDataChunkArea<T>;
+    | TooltipDataChunkArea<T>
+    | TooltipDataChunkTreemap<T>;
 
 export type ChartKitWidgetTooltip<T = any> = {
     enabled?: boolean;
diff --git a/src/types/widget-data/treemap.ts b/src/types/widget-data/treemap.ts
new file mode 100644
index 00000000..b26282ec
--- /dev/null
+++ b/src/types/widget-data/treemap.ts
@@ -0,0 +1,40 @@
+import {LayoutAlgorithm, SeriesType} from '../../constants';
+import type {BaseSeries, BaseSeriesData} from './base';
+import {ChartKitWidgetLegend, RectLegendSymbolOptions} from './legend';
+
+export type TreemapSeriesData<T = any> = BaseSeriesData<T> & {
+    /** The name of the node (used in legend, tooltip etc). */
+    name: string;
+    /** The value of the node. All nodes should have this property except nodes that have children. */
+    value?: number;
+    /** An id for the node. Used to group children. */
+    id?: string;
+    /**
+     * Parent id. Used to build a tree structure. The value should be the id of the node which is the parent.
+     * If no nodes has a matching id, or this option is undefined, then the parent will be set to the root.
+     */
+    parentId?: string;
+};
+
+export type TreemapSeries<T = any> = BaseSeries & {
+    type: typeof SeriesType.Treemap;
+    data: TreemapSeriesData<T>[];
+    /** The name of the series (used in legend, tooltip etc). */
+    name: string;
+    /** The main color of the series (hex, rgba). */
+    color?: string;
+    /** Individual series legend options. Has higher priority than legend options in widget data. */
+    legend?: ChartKitWidgetLegend & {
+        symbol?: RectLegendSymbolOptions;
+    };
+    /** Set options on specific levels. Takes precedence over series options, but not point options. */
+    levels?: {
+        /** Decides which level takes effect from the options set in the levels object. */
+        index: number;
+        /** Can set the padding between all points which lies on the same level. */
+        padding?: number;
+        /** Can set a color on all points which lies on the same level. */
+        color?: string;
+    }[];
+    layoutAlgorithm?: `${LayoutAlgorithm}`;
+};