diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
index b4bce8d7982fe4d1a52e4c7277d340c3b6f33b91..66ddd44856ee26ccc4b789ec09e1a5cddf894db5 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
@@ -6,7 +6,11 @@ import { useEffect, useMemo } from 'react';
 import { usePointToProjection } from '@/utils/map/usePointToProjection';
 import MapElement from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement';
 import { useSelector } from 'react-redux';
-import { bioShapesSelector, lineTypesSelector } from '@/redux/shapes/shapes.selectors';
+import {
+  arrowTypesSelector,
+  bioShapesSelector,
+  lineTypesSelector,
+} from '@/redux/shapes/shapes.selectors';
 import { MapInstance } from '@/types/map';
 import {
   HorizontalAlign,
@@ -21,6 +25,9 @@ import CompartmentCircle from '@/components/Map/MapViewer/MapViewerVector/utils/
 import { ModelElement } from '@/types/models';
 import Glyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph';
 import CompartmentPathway from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway';
+import Reaction from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction';
+import { newReactionsDataSelector } from '@/redux/newReactions/newReactions.selectors';
+import { getNewReactions } from '@/redux/newReactions/newReactions.thunks';
 
 export const useOlMapReactionsLayer = ({
   mapInstance,
@@ -29,14 +36,32 @@ export const useOlMapReactionsLayer = ({
 }): VectorLayer<VectorSource<Feature>> => {
   const dispatch = useAppDispatch();
   const modelElements = useSelector(modelElementsSelector);
+  const modelReactions = useSelector(newReactionsDataSelector);
   const currentModelId = useSelector(currentModelIdSelector);
   useEffect(() => {
     dispatch(getModelElements(currentModelId));
+    dispatch(getNewReactions(currentModelId));
   }, [currentModelId, dispatch]);
 
   const pointToProjection = usePointToProjection();
   const shapes = useSelector(bioShapesSelector);
   const lineTypes = useSelector(lineTypesSelector);
+  const arrowTypes = useSelector(arrowTypesSelector);
+
+  const reactions = useMemo(() => {
+    return modelReactions.map(reaction => {
+      const reactionObject = new Reaction({
+        line: reaction.line,
+        products: reaction.products,
+        reactants: reaction.reactants,
+        zIndex: reaction.z,
+        lineTypes,
+        arrowTypes,
+        pointToProjection,
+      });
+      return reactionObject.features;
+    });
+  }, [arrowTypes, lineTypes, modelReactions, pointToProjection]);
 
   const elements: Array<
     MapElement | CompartmentCircle | CompartmentSquare | CompartmentPathway | Glyph
@@ -135,8 +160,10 @@ export const useOlMapReactionsLayer = ({
   }, [modelElements, shapes, pointToProjection, mapInstance, lineTypes]);
 
   const features = useMemo(() => {
-    return elements.map(element => element.feature);
-  }, [elements]);
+    const reactionsFeatures = reactions.flat();
+    const elementsFeatures = elements.map(element => element.feature);
+    return [...reactionsFeatures, ...elementsFeatures];
+  }, [elements, reactions]);
 
   const vectorSource = useMemo(() => {
     return new VectorSource({
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts
index 4c0a4aff44904869a45a28ffc97efda41fa235ad..70fb9ce7bc355513eb4d16875dfb1ab618962962 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts
@@ -3,7 +3,7 @@ import { Feature, Map } from 'ol';
 import { Fill, Style, Text } from 'ol/style';
 import { Polygon, MultiPolygon } from 'ol/geom';
 import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
-import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
+import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
 import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
 import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
 import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
@@ -20,7 +20,7 @@ import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shap
 
 jest.mock('../text/getTextStyle');
 jest.mock('../text/getTextCoords');
-jest.mock('./getMultiPolygon');
+jest.mock('./getShapePolygon');
 jest.mock('../style/getStroke');
 jest.mock('../coords/getEllipseCoords');
 jest.mock('../style/getFill');
@@ -78,7 +78,7 @@ describe('MapElement', () => {
       }),
     );
     (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
-    (getMultiPolygon as jest.Mock).mockReturnValue([
+    (getShapePolygon as jest.Mock).mockReturnValue(
       new Polygon([
         [
           [0, 0],
@@ -86,7 +86,7 @@ describe('MapElement', () => {
           [2, 2],
         ],
       ]),
-    ]);
+    );
     (getStroke as jest.Mock).mockReturnValue(new Style());
     (getFill as jest.Mock).mockReturnValue(new Style());
     (rgbToHex as jest.Mock).mockReturnValue('#FFFFFF');
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts
index c47c01b5d9ab60c0ae118558e383f80434018286..caa015c610e4c6c65b282acae535e5cf462899bd 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts
@@ -3,7 +3,7 @@ import { Feature, Map } from 'ol';
 import { Fill, Style, Text } from 'ol/style';
 import { Polygon, MultiPolygon } from 'ol/geom';
 import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
-import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
+import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
 import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
 import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
 import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
@@ -20,7 +20,7 @@ import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shap
 
 jest.mock('../text/getTextStyle');
 jest.mock('../text/getTextCoords');
-jest.mock('./getMultiPolygon');
+jest.mock('./getShapePolygon');
 jest.mock('../style/getStroke');
 jest.mock('../coords/getEllipseCoords');
 jest.mock('../style/getFill');
@@ -76,7 +76,7 @@ describe('MapElement', () => {
       }),
     );
     (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
-    (getMultiPolygon as jest.Mock).mockReturnValue([
+    (getShapePolygon as jest.Mock).mockReturnValue(
       new Polygon([
         [
           [0, 0],
@@ -84,7 +84,7 @@ describe('MapElement', () => {
           [2, 2],
         ],
       ]),
-    ]);
+    );
     (getStroke as jest.Mock).mockReturnValue(new Style());
     (getFill as jest.Mock).mockReturnValue(new Style());
     (rgbToHex as jest.Mock).mockReturnValue('#FFFFFF');
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts
index a368e292ba6429c0195f071ee4bc19df78044bd2..17bc27013654817113e830a393c49269ec8eccd9 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts
@@ -1,9 +1,8 @@
 /* eslint-disable no-magic-numbers */
 import { Feature, Map } from 'ol';
 import { Fill, Style, Text } from 'ol/style';
-import { Polygon, MultiPolygon } from 'ol/geom';
+import { MultiPolygon } from 'ol/geom';
 import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
-import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
 import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
 import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
 import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
@@ -19,7 +18,6 @@ import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/s
 import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
 
 jest.mock('../text/getTextStyle');
-jest.mock('./getMultiPolygon');
 jest.mock('../text/getTextCoords');
 jest.mock('../style/getStroke');
 jest.mock('../coords/getPolygonCoords');
@@ -78,15 +76,6 @@ describe('MapElement', () => {
       }),
     );
     (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
-    (getMultiPolygon as jest.Mock).mockReturnValue([
-      new Polygon([
-        [
-          [0, 0],
-          [1, 1],
-          [2, 2],
-        ],
-      ]),
-    ]);
     (getStroke as jest.Mock).mockReturnValue(new Style());
     (getFill as jest.Mock).mockReturnValue(new Style());
     (rgbToHex as jest.Mock).mockReturnValue('#FFFFFF');
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
index 5b38d5851545cf5cfc6412a2a37d3b6af0a69102..eddaf77da5a4bc09713ef1c1247e425ef5a6cbba 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
@@ -3,7 +3,7 @@ import { Feature, Map } from 'ol';
 import { Fill, Style, Text } from 'ol/style';
 import { Polygon, MultiPolygon } from 'ol/geom';
 import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
-import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
+import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
 import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
 import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
 import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
@@ -16,10 +16,11 @@ import {
   BLACK_COLOR,
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
+import { shapesFixture } from '@/models/fixtures/shapesFixture';
 
 jest.mock('../text/getTextStyle');
 jest.mock('../text/getTextCoords');
-jest.mock('./getMultiPolygon');
+jest.mock('./getShapePolygon');
 jest.mock('../style/getStroke');
 jest.mock('../style/getFill');
 jest.mock('../style/rgbToHex');
@@ -38,7 +39,7 @@ describe('MapElement', () => {
       }),
     });
     props = {
-      shapes: [],
+      shapes: shapesFixture,
       x: 0,
       y: 0,
       width: 100,
@@ -75,7 +76,7 @@ describe('MapElement', () => {
       }),
     );
     (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
-    (getMultiPolygon as jest.Mock).mockReturnValue([
+    (getShapePolygon as jest.Mock).mockReturnValue(
       new Polygon([
         [
           [0, 0],
@@ -83,7 +84,7 @@ describe('MapElement', () => {
           [2, 2],
         ],
       ]),
-    ]);
+    );
     (getStroke as jest.Mock).mockReturnValue(new Style());
     (getFill as jest.Mock).mockReturnValue(new Style());
     (rgbToHex as jest.Mock).mockReturnValue('#FFFFFF');
@@ -92,7 +93,7 @@ describe('MapElement', () => {
   it('should initialize with correct default properties', () => {
     const multiPolygon = new MapElement(props);
 
-    expect(multiPolygon.polygons.length).toBe(2);
+    expect(multiPolygon.polygons.length).toBe(3);
     expect(multiPolygon.feature).toBeInstanceOf(Feature);
     expect(multiPolygon.feature.getGeometry()).toBeInstanceOf(MultiPolygon);
   });
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
index b9fc0551e99f00892cc0480b70a5b2e706310ea9..4dc8eebc89f334166598c01119111cc9f32ceb42 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
@@ -4,7 +4,6 @@ import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
 import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
 import Polygon from 'ol/geom/Polygon';
-import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
 import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
 import { BioShape, Color, LineType, Modification, Shape } from '@/types/models';
 import { MapInstance } from '@/types/map';
@@ -20,6 +19,7 @@ import BaseMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/s
 import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
 import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
 import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
 
 export type MapElementProps = {
   shapes: Array<Shape>;
@@ -160,25 +160,26 @@ export default class MapElement extends BaseMultiPolygon {
   }
 
   drawModification(modification: Modification, shapes: Array<Shape>): void {
-    const multiPolygonModification = getMultiPolygon({
-      x: modification.x,
-      y: modification.y,
-      width: modification.width,
-      height: modification.height,
-      shapes,
-      pointToProjection: this.pointToProjection,
-      mirror: modification.direction && modification.direction === 'RIGHT',
-    });
-    this.polygons.push(...multiPolygonModification);
-    multiPolygonModification.forEach((polygon: Polygon) => {
+    shapes.forEach(shape => {
+      const modificationPolygon = getShapePolygon({
+        shape,
+        x: modification.x,
+        y: modification.y,
+        width: modification.width,
+        height: modification.height,
+        pointToProjection: this.pointToProjection,
+        mirror: modification.direction && modification.direction === 'RIGHT',
+      });
       const modificationStyle = new Style({
-        geometry: polygon,
+        geometry: modificationPolygon,
         stroke: getStroke({ color: rgbToHex(modification.borderColor) }),
         fill: getFill({ color: rgbToHex(modification.fillColor) }),
         zIndex: modification.z,
       });
+      this.polygons.push(modificationPolygon);
       this.styles.push(modificationStyle);
     });
+
     const modificationText = modification.stateAbbreviation
       ? modification.stateAbbreviation
       : modification.name;
@@ -211,48 +212,46 @@ export default class MapElement extends BaseMultiPolygon {
   }
 
   drawActiveBorder(homodimerShift: number, homodimerOffset: number): void {
-    const activityBorderElement = getMultiPolygon({
-      x: this.x + homodimerShift - 5,
-      y: this.y + homodimerShift - 5,
-      width: this.width - homodimerOffset + 10,
-      height: this.height - homodimerOffset + 10,
-      shapes: this.shapes,
-      pointToProjection: this.pointToProjection,
-    });
-    activityBorderElement.forEach(polygon => {
-      this.styles.push(
-        getStyle({
-          geometry: polygon,
-          fillColor: { rgb: 0, alpha: 0 },
-          lineDash: [3, 5],
-          zIndex: this.zIndex,
-        }),
-      );
+    this.shapes.forEach(shape => {
+      const activityBorderPolygon = getShapePolygon({
+        shape,
+        x: this.x + homodimerShift - 5,
+        y: this.y + homodimerShift - 5,
+        width: this.width - homodimerOffset + 10,
+        height: this.height - homodimerOffset + 10,
+        pointToProjection: this.pointToProjection,
+      });
+      const activityBorderStyle = getStyle({
+        geometry: activityBorderPolygon,
+        fillColor: { rgb: 0, alpha: 0 },
+        lineDash: [3, 5],
+        zIndex: this.zIndex,
+      });
+      this.polygons.push(activityBorderPolygon);
+      this.styles.push(activityBorderStyle);
     });
-    this.polygons.push(...activityBorderElement);
   }
 
   drawElementPolygon(homodimerShift: number, homodimerOffset: number): void {
-    const elementPolygon = getMultiPolygon({
-      x: this.x + homodimerShift,
-      y: this.y + homodimerShift,
-      width: this.width - homodimerOffset,
-      height: this.height - homodimerOffset,
-      shapes: this.shapes,
-      pointToProjection: this.pointToProjection,
-    });
-    elementPolygon.forEach(polygon => {
-      this.styles.push(
-        getStyle({
-          geometry: polygon,
-          borderColor: this.borderColor,
-          fillColor: this.fillColor,
-          lineWidth: this.lineWidth,
-          lineDash: this.lineDash,
-          zIndex: this.zIndex,
-        }),
-      );
+    this.shapes.forEach(shape => {
+      const elementPolygon = getShapePolygon({
+        shape,
+        x: this.x + homodimerShift,
+        y: this.y + homodimerShift,
+        width: this.width - homodimerOffset,
+        height: this.height - homodimerOffset,
+        pointToProjection: this.pointToProjection,
+      });
+      const elementStyle = getStyle({
+        geometry: elementPolygon,
+        borderColor: this.borderColor,
+        fillColor: this.fillColor,
+        lineWidth: this.lineWidth,
+        lineDash: this.lineDash,
+        zIndex: this.zIndex,
+      });
+      this.polygons.push(elementPolygon);
+      this.styles.push(elementStyle);
     });
-    this.polygons.push(...elementPolygon);
   }
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f94790c7a4ba06ec06d4c859b4dfb8c19d95d4a6
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.test.ts
@@ -0,0 +1,125 @@
+/* eslint-disable no-magic-numbers */
+import { Feature } from 'ol';
+import { Fill, Stroke, Style } from 'ol/style';
+import { Polygon, MultiPolygon } from 'ol/geom';
+import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
+import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
+import getArrowFeature from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature';
+import { ArrowType } from '@/types/models';
+import { BLACK_COLOR } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+
+jest.mock('../style/getStyle');
+jest.mock('./getShapePolygon');
+
+describe('getArrowFeature', () => {
+  const props = {
+    arrowTypes: [
+      {
+        arrowType: 'FULL',
+        shapes: [
+          {
+            type: 'POLYGON',
+            fill: false,
+            points: [
+              {
+                type: 'REL_ABS_POINT',
+                absoluteX: 0.0,
+                absoluteY: 0.0,
+                relativeX: 0.0,
+                relativeY: 50.0,
+                relativeHeightForX: null,
+                relativeWidthForY: null,
+              },
+              {
+                type: 'REL_ABS_POINT',
+                absoluteX: 0.0,
+                absoluteY: 0.0,
+                relativeX: 100.0,
+                relativeY: 50.0,
+                relativeHeightForX: null,
+                relativeWidthForY: null,
+              },
+              {
+                type: 'REL_ABS_POINT',
+                absoluteX: 0.0,
+                absoluteY: 0.0,
+                relativeX: 7.612046748871326,
+                relativeY: 50.0,
+                relativeHeightForX: null,
+                relativeWidthForY: 19.134171618254495,
+              },
+              {
+                type: 'REL_ABS_POINT',
+                absoluteX: 0.0,
+                absoluteY: 0.0,
+                relativeX: 7.612046748871326,
+                relativeY: 50.0,
+                relativeHeightForX: null,
+                relativeWidthForY: -19.134171618254495,
+              },
+              {
+                type: 'REL_ABS_POINT',
+                absoluteX: 0.0,
+                absoluteY: 0.0,
+                relativeX: 100.0,
+                relativeY: 50.0,
+                relativeHeightForX: null,
+                relativeWidthForY: null,
+              },
+            ],
+          },
+        ],
+      } as ArrowType,
+    ],
+    arrow: { length: 15, arrowType: 'FULL', angle: 2.74, lineType: 'SOLID' },
+    x: 0,
+    y: 0,
+    zIndex: 1,
+    rotation: 1,
+    lineWidth: 1,
+    color: BLACK_COLOR,
+    pointToProjection: jest.fn(() => [10, 10]),
+  };
+  const polygon = new Polygon([
+    [
+      [0, 0],
+      [1, 1],
+      [2, 2],
+    ],
+  ]);
+
+  beforeEach(() => {
+    (getStyle as jest.Mock).mockReturnValue(
+      new Style({
+        geometry: polygon,
+        stroke: new Stroke({}),
+        fill: new Fill({}),
+      }),
+    );
+    (getShapePolygon as jest.Mock).mockReturnValue(
+      new Polygon([
+        [
+          [0, 0],
+          [1, 1],
+          [2, 2],
+        ],
+      ]),
+    );
+  });
+
+  it('should return arrow feature', () => {
+    const arrowFeature = getArrowFeature(props);
+
+    expect(arrowFeature).toBeInstanceOf(Feature<MultiPolygon>);
+  });
+
+  it('should handle missing arrowType in props', () => {
+    const invalidProps = { ...props, arrowTypes: [] };
+    expect(() => getArrowFeature(invalidProps)).not.toThrow();
+  });
+
+  it('should call getStyle', () => {
+    getArrowFeature(props);
+    expect(getStyle).toBeCalled();
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7d7e53d60d3db92f920399748a88e41eb0c7514b
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.ts
@@ -0,0 +1,63 @@
+/* eslint-disable no-magic-numbers */
+import Style from 'ol/style/Style';
+import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
+import { Feature } from 'ol';
+import { MultiPolygon } from 'ol/geom';
+import { Arrow, ArrowType, Color } from '@/types/models';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
+import Polygon from 'ol/geom/Polygon';
+
+export default function getArrowFeature({
+  arrowTypes,
+  arrow,
+  x,
+  y,
+  zIndex,
+  rotation,
+  lineWidth,
+  color,
+  pointToProjection,
+}: {
+  arrowTypes: Array<ArrowType>;
+  arrow: Arrow;
+  x: number;
+  y: number;
+  zIndex: number;
+  rotation: number;
+  lineWidth: number;
+  color: Color;
+  pointToProjection: UsePointToProjectionResult;
+}): undefined | Feature<MultiPolygon> {
+  const arrowShapes = arrowTypes.find(arrowType => arrowType.arrowType === arrow.arrowType)?.shapes;
+  if (!arrowShapes) {
+    return undefined;
+  }
+  const arrowStyles: Array<Style> = [];
+  const arrowPolygons: Array<Polygon> = [];
+  arrowShapes.forEach(shape => {
+    const arrowPolygon = getShapePolygon({
+      shape,
+      x,
+      y: y - arrow.length / 2,
+      width: arrow.length,
+      height: arrow.length,
+      pointToProjection,
+    });
+    const style = getStyle({
+      geometry: arrowPolygon,
+      zIndex,
+      borderColor: color,
+      fillColor: shape.fill === false ? undefined : color,
+      lineWidth,
+    });
+    arrowPolygon.rotate(rotation, pointToProjection({ x, y }));
+    arrowStyles.push(style);
+    arrowPolygons.push(arrowPolygon);
+  });
+  const arrowFeature = new Feature({
+    geometry: new MultiPolygon(arrowPolygons),
+  });
+  arrowFeature.setStyle(arrowStyles);
+  return arrowFeature;
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.ts
deleted file mode 100644
index d91c741abd1a4e477e3c5f6ed09a4fafad650b11..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords';
-import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
-import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
-import Polygon from 'ol/geom/Polygon';
-import { Coordinate } from 'ol/coordinate';
-import { Shape } from '@/types/models';
-import getCentroid from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCentroid';
-
-export default function getMultiPolygon({
-  x,
-  y,
-  width,
-  height,
-  shapes,
-  pointToProjection,
-  mirror,
-}: {
-  x: number;
-  y: number;
-  width: number;
-  height: number;
-  shapes: Array<Shape>;
-  pointToProjection: UsePointToProjectionResult;
-  mirror?: boolean;
-}): Array<Polygon> {
-  return shapes.map(shape => {
-    let coords: Array<Coordinate> = [];
-    if (shape.type === 'POLYGON') {
-      coords = getPolygonCoords({ points: shape.points, x, y, height, width, pointToProjection });
-    } else if (shape.type === 'ELLIPSE') {
-      coords = getEllipseCoords({
-        x,
-        y,
-        center: shape.center,
-        radius: shape.radius,
-        height,
-        width,
-        pointToProjection,
-      });
-    }
-
-    if (mirror) {
-      const centroid = getCentroid(coords);
-
-      coords = coords.map(coord => {
-        const mirroredX = 2 * centroid[0] - coord[0];
-
-        return [mirroredX, coord[1]];
-      });
-    }
-
-    return new Polygon([coords]);
-  });
-}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.test.ts
similarity index 87%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.test.ts
index b4d697f3bfa5d3888af75c238ca1d2231716c803..0074f57be0512f7db5b60f44e7702a0deb85afb6 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.test.ts
@@ -3,12 +3,12 @@ import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/s
 import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
 import Polygon from 'ol/geom/Polygon';
 import { Shape } from '@/types/models';
-import getMultiPolygon from './getMultiPolygon';
+import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
 
 jest.mock('../coords/getPolygonCoords');
 jest.mock('../coords/getEllipseCoords');
 
-describe('getMultiPolygon', () => {
+describe('getShapePolygon', () => {
   const mockPointToProjection = jest.fn(point => [point.x, point.y]);
 
   beforeEach(() => {
@@ -75,15 +75,16 @@ describe('getMultiPolygon', () => {
     ];
     (getPolygonCoords as jest.Mock).mockReturnValue(mockPolygonCoords);
 
-    const result = getMultiPolygon({
-      x,
-      y,
-      width,
-      height,
-      shapes,
-      pointToProjection: mockPointToProjection,
+    const result = shapes.map(shape => {
+      return getShapePolygon({
+        shape,
+        x,
+        y,
+        width,
+        height,
+        pointToProjection: mockPointToProjection,
+      });
     });
-
     expect(result).toHaveLength(1);
     expect(result[0]).toBeInstanceOf(Polygon);
     expect(result[0].getCoordinates()).toEqual([mockPolygonCoords]);
@@ -133,13 +134,15 @@ describe('getMultiPolygon', () => {
     ];
     (getEllipseCoords as jest.Mock).mockReturnValue(mockEllipseCoords);
 
-    const result = getMultiPolygon({
-      x,
-      y,
-      width,
-      height,
-      shapes,
-      pointToProjection: mockPointToProjection,
+    const result = shapes.map(shape => {
+      return getShapePolygon({
+        shape,
+        x,
+        y,
+        width,
+        height,
+        pointToProjection: mockPointToProjection,
+      });
     });
 
     expect(result).toHaveLength(1);
@@ -223,13 +226,15 @@ describe('getMultiPolygon', () => {
     (getPolygonCoords as jest.Mock).mockReturnValue(mockPolygonCoords);
     (getEllipseCoords as jest.Mock).mockReturnValue(mockEllipseCoords);
 
-    const result = getMultiPolygon({
-      x,
-      y,
-      width,
-      height,
-      shapes,
-      pointToProjection: mockPointToProjection,
+    const result = shapes.map(shape => {
+      return getShapePolygon({
+        shape,
+        x,
+        y,
+        width,
+        height,
+        pointToProjection: mockPointToProjection,
+      });
     });
 
     expect(result).toHaveLength(2);
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f2a5280f8284c0dd5c73f0bf9c421319bb1eb78
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.ts
@@ -0,0 +1,53 @@
+/* eslint-disable no-magic-numbers */
+import Polygon from 'ol/geom/Polygon';
+import { Coordinate } from 'ol/coordinate';
+import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords';
+import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
+import getCentroid from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCentroid';
+import { Shape } from '@/types/models';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+
+export default function getShapePolygon({
+  shape,
+  x,
+  y,
+  width,
+  height,
+  mirror,
+  pointToProjection,
+}: {
+  shape: Shape;
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  mirror?: boolean;
+  pointToProjection: UsePointToProjectionResult;
+}): Polygon {
+  let coords: Array<Coordinate> = [];
+  if (shape.type === 'POLYGON') {
+    coords = getPolygonCoords({ points: shape.points, x, y, height, width, pointToProjection });
+  } else if (shape.type === 'ELLIPSE') {
+    coords = getEllipseCoords({
+      x,
+      y,
+      center: shape.center,
+      radius: shape.radius,
+      height,
+      width,
+      pointToProjection,
+    });
+  }
+
+  if (mirror) {
+    const centroid = getCentroid(coords);
+
+    coords = coords.map(coord => {
+      const mirroredX = 2 * centroid[0] - coord[0];
+
+      return [mirroredX, coord[1]];
+    });
+  }
+
+  return new Polygon([coords]);
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
index c6322fef57135c9667c283129883f050d1856580..a9adc4d4298e6f353262c251fc499fce19c64f15 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
@@ -1,13 +1,5 @@
 /* eslint-disable no-magic-numbers */
-import {
-  Arrow,
-  ArrowType,
-  LayerLine,
-  LayerOval,
-  LayerRect,
-  LayerText,
-  LineType,
-} from '@/types/models';
+import { ArrowType, LayerLine, LayerOval, LayerRect, LayerText, LineType } from '@/types/models';
 import { MapInstance } from '@/types/map';
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import { Feature } from 'ol';
@@ -22,10 +14,8 @@ import {
   HorizontalAlign,
   VerticalAlign,
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
-import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
-import Style from 'ol/style/Style';
-import { BLACK_COLOR } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import getRotation from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation';
+import getArrowFeature from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature';
 
 export interface LayerProps {
   texts: Array<LayerText>;
@@ -63,6 +53,8 @@ export default class Layer {
 
   arrowFeatures: Array<Feature<MultiPolygon>>;
 
+  pointToProjection: UsePointToProjectionResult;
+
   vectorSource: VectorSource<
     Feature<Point> | Feature<Polygon> | Feature<LineString> | Feature<MultiPolygon>
   >;
@@ -89,10 +81,11 @@ export default class Layer {
     this.lines = lines;
     this.lineTypes = lineTypes;
     this.arrowTypes = arrowTypes;
-    this.textFeatures = this.getTextsFeatures(mapInstance, pointToProjection);
-    this.rectFeatures = this.getRectsFeatures(pointToProjection);
-    this.ovalFeatures = this.getOvalsFeatures(pointToProjection);
-    const { linesFeatures, arrowsFeatures } = this.getLinesFeatures(pointToProjection);
+    this.pointToProjection = pointToProjection;
+    this.textFeatures = this.getTextsFeatures(mapInstance);
+    this.rectFeatures = this.getRectsFeatures();
+    this.ovalFeatures = this.getOvalsFeatures();
+    const { linesFeatures, arrowsFeatures } = this.getLinesFeatures();
     this.lineFeatures = linesFeatures;
     this.arrowFeatures = arrowsFeatures;
     this.vectorSource = new VectorSource({
@@ -111,10 +104,7 @@ export default class Layer {
     this.vectorLayer.set('id', layerId);
   }
 
-  private getTextsFeatures = (
-    mapInstance: MapInstance,
-    pointToProjection: UsePointToProjectionResult,
-  ): Array<Feature<Point>> => {
+  private getTextsFeatures = (mapInstance: MapInstance): Array<Feature<Point>> => {
     const textObjects = this.texts.map(text => {
       return new Text({
         x: text.x,
@@ -127,23 +117,21 @@ export default class Layer {
         text: text.notes,
         verticalAlign: text.verticalAlign as VerticalAlign,
         horizontalAlign: text.horizontalAlign as HorizontalAlign,
-        pointToProjection,
+        pointToProjection: this.pointToProjection,
         mapInstance,
       });
     });
     return textObjects.map(text => text.feature);
   };
 
-  private getRectsFeatures = (
-    pointToProjection: UsePointToProjectionResult,
-  ): Array<Feature<Polygon>> => {
+  private getRectsFeatures = (): Array<Feature<Polygon>> => {
     return this.rects.map(rect => {
       const polygon = new Polygon([
         [
-          pointToProjection({ x: rect.x, y: rect.y }),
-          pointToProjection({ x: rect.x + rect.width, y: rect.y }),
-          pointToProjection({ x: rect.x + rect.width, y: rect.y + rect.height }),
-          pointToProjection({ x: rect.x, y: rect.y + rect.height }),
+          this.pointToProjection({ x: rect.x, y: rect.y }),
+          this.pointToProjection({ x: rect.x + rect.width, y: rect.y }),
+          this.pointToProjection({ x: rect.x + rect.width, y: rect.y + rect.height }),
+          this.pointToProjection({ x: rect.x, y: rect.y + rect.height }),
         ],
       ]);
       const polygonStyle = getStyle({
@@ -161,16 +149,14 @@ export default class Layer {
     });
   };
 
-  private getOvalsFeatures = (
-    pointToProjection: UsePointToProjectionResult,
-  ): Array<Feature<Polygon>> => {
+  private getOvalsFeatures = (): Array<Feature<Polygon>> => {
     return this.ovals.map(oval => {
       const coords = getEllipseCoords({
         x: oval.x + oval.width / 2,
         y: oval.y + oval.height / 2,
         height: oval.height,
         width: oval.width,
-        pointToProjection,
+        pointToProjection: this.pointToProjection,
         points: 36,
       });
       const polygon = new Polygon([coords]);
@@ -189,9 +175,7 @@ export default class Layer {
     });
   };
 
-  private getLinesFeatures = (
-    pointToProjection: UsePointToProjectionResult,
-  ): {
+  private getLinesFeatures = (): {
     linesFeatures: Array<Feature<LineString>>;
     arrowsFeatures: Array<Feature<MultiPolygon>>;
   } => {
@@ -203,31 +187,65 @@ export default class Layer {
         .map((segment, index) => {
           if (index === 0) {
             return [
-              pointToProjection({ x: segment.x1, y: segment.y1 }),
-              pointToProjection({ x: segment.x2, y: segment.y2 }),
+              this.pointToProjection({ x: segment.x1, y: segment.y1 }),
+              this.pointToProjection({ x: segment.x2, y: segment.y2 }),
             ];
           }
-          return [pointToProjection({ x: segment.x2, y: segment.y2 })];
+          return [this.pointToProjection({ x: segment.x2, y: segment.y2 })];
         })
         .flat();
 
-      const firstSegment = line.segments[0];
-      const startArrowRotation = getRotation(
-        [firstSegment.x1, firstSegment.y1],
-        [firstSegment.x2, firstSegment.y2],
-      );
-      const shortenedX1 = firstSegment.x1 + line.startArrow.length * Math.cos(startArrowRotation);
-      const shortenedY1 = firstSegment.y1 - line.startArrow.length * Math.sin(startArrowRotation);
-      points[0] = pointToProjection({ x: shortenedX1, y: shortenedY1 });
+      if (line.startArrow.arrowType !== 'NONE') {
+        const firstSegment = line.segments[0];
+        const startArrowRotation = getRotation(
+          [firstSegment.x1, firstSegment.y1],
+          [firstSegment.x2, firstSegment.y2],
+        );
+        const shortenedX1 = firstSegment.x1 + line.startArrow.length * Math.cos(startArrowRotation);
+        const shortenedY1 = firstSegment.y1 - line.startArrow.length * Math.sin(startArrowRotation);
+        points[0] = this.pointToProjection({ x: shortenedX1, y: shortenedY1 });
+
+        const startArrowFeature = getArrowFeature({
+          arrowTypes: this.arrowTypes,
+          arrow: line.startArrow,
+          x: shortenedX1,
+          y: shortenedY1,
+          zIndex: line.z,
+          rotation: startArrowRotation,
+          lineWidth: line.width,
+          color: line.color,
+          pointToProjection: this.pointToProjection,
+        });
+        if (startArrowFeature) {
+          arrowsFeatures.push(startArrowFeature);
+        }
+      }
+
+      if (line.endArrow.arrowType !== 'NONE') {
+        const lastSegment = line.segments[line.segments.length - 1];
+        const endArrowRotation = getRotation(
+          [lastSegment.x1, lastSegment.y1],
+          [lastSegment.x2, lastSegment.y2],
+        );
+        const shortenedX2 = lastSegment.x2 - line.endArrow.length * Math.cos(endArrowRotation);
+        const shortenedY2 = lastSegment.y2 - line.endArrow.length * Math.sin(endArrowRotation);
+        points[points.length - 1] = this.pointToProjection({ x: shortenedX2, y: shortenedY2 });
 
-      const lastSegment = line.segments[line.segments.length - 1];
-      const endArrowRotation = getRotation(
-        [lastSegment.x1, lastSegment.y1],
-        [lastSegment.x2, lastSegment.y2],
-      );
-      const shortenedX2 = lastSegment.x2 - line.endArrow.length * Math.cos(endArrowRotation);
-      const shortenedY2 = lastSegment.y2 - line.endArrow.length * Math.sin(endArrowRotation);
-      points[points.length - 1] = pointToProjection({ x: shortenedX2, y: shortenedY2 });
+        const endArrowFeature = getArrowFeature({
+          arrowTypes: this.arrowTypes,
+          arrow: line.endArrow,
+          x: shortenedX2,
+          y: shortenedY2,
+          zIndex: line.z,
+          rotation: endArrowRotation,
+          lineWidth: line.width,
+          color: line.color,
+          pointToProjection: this.pointToProjection,
+        });
+        if (endArrowFeature) {
+          arrowsFeatures.push(endArrowFeature);
+        }
+      }
 
       const lineString = new LineString(points);
 
@@ -248,109 +266,7 @@ export default class Layer {
       });
       lineFeature.setStyle(lineStyle);
       linesFeatures.push(lineFeature);
-
-      arrowsFeatures.push(
-        ...this.getLineArrowsFeatures({
-          line,
-          pointToProjection,
-          startArrowX: firstSegment.x1,
-          startArrowY: firstSegment.y1,
-          startArrowRotation,
-          endArrowX: shortenedX2,
-          endArrowY: shortenedY2,
-          endArrowRotation,
-        }),
-      );
     });
     return { linesFeatures, arrowsFeatures };
   };
-
-  private getLineArrowsFeatures = ({
-    line,
-    pointToProjection,
-    startArrowX,
-    startArrowY,
-    startArrowRotation,
-    endArrowX,
-    endArrowY,
-    endArrowRotation,
-  }: {
-    line: LayerLine;
-    pointToProjection: UsePointToProjectionResult;
-    startArrowX: number;
-    startArrowY: number;
-    startArrowRotation: number;
-    endArrowX: number;
-    endArrowY: number;
-    endArrowRotation: number;
-  }): Array<Feature<MultiPolygon>> => {
-    const arrowsFeatures: Array<Feature<MultiPolygon>> = [];
-    const startArrowFeature = this.getLineArrowFeature(
-      line.startArrow,
-      startArrowX,
-      startArrowY,
-      line.z,
-      startArrowRotation,
-      line.width,
-      pointToProjection,
-    );
-    if (startArrowFeature) {
-      arrowsFeatures.push(startArrowFeature);
-    }
-
-    const endArrowFeature = this.getLineArrowFeature(
-      line.endArrow,
-      endArrowX,
-      endArrowY,
-      line.z,
-      endArrowRotation,
-      line.width,
-      pointToProjection,
-    );
-    if (endArrowFeature) {
-      arrowsFeatures.push(endArrowFeature);
-    }
-    return arrowsFeatures;
-  };
-
-  private getLineArrowFeature = (
-    arrow: Arrow,
-    x: number,
-    y: number,
-    zIndex: number,
-    rotation: number,
-    lineWidth: number,
-    pointToProjection: UsePointToProjectionResult,
-  ): undefined | Feature<MultiPolygon> => {
-    const arrowShapes = this.arrowTypes.find(arrowType => arrowType.arrowType === arrow.arrowType)
-      ?.shapes;
-    if (!arrowShapes) {
-      return undefined;
-    }
-    const arrowMultiPolygon = getMultiPolygon({
-      x,
-      y: y - arrow.length / 2,
-      width: arrow.length,
-      height: arrow.length,
-      shapes: arrowShapes,
-      pointToProjection,
-    });
-    const arrowStyles: Array<Style> = [];
-    arrowMultiPolygon.forEach(polygon => {
-      const style = getStyle({
-        geometry: polygon,
-        zIndex,
-        borderColor: BLACK_COLOR,
-        fillColor: BLACK_COLOR,
-        lineWidth,
-      });
-      arrowStyles.push(style);
-      polygon.rotate(rotation, pointToProjection({ x, y }));
-    });
-    const arrowFeature = new Feature({
-      geometry: new MultiPolygon(arrowMultiPolygon),
-    });
-    arrowFeature.setStyle(arrowStyles);
-    return arrowFeature;
-  };
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b3d84f4b244a5c1f52d4e24580c57ddb3bdc9ca7
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts
@@ -0,0 +1,31 @@
+/* eslint-disable no-magic-numbers */
+import { Feature } from 'ol';
+import Reaction, {
+  ReactionProps,
+} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction';
+import { newReactionFixture } from '@/models/fixtures/newReactionFixture';
+import { lineTypesFixture } from '@/models/fixtures/lineTypesFixture';
+import { arrowTypesFixture } from '@/models/fixtures/arrowTypesFixture';
+
+describe('Layer', () => {
+  let props: ReactionProps;
+
+  beforeEach(() => {
+    props = {
+      line: newReactionFixture.line,
+      products: newReactionFixture.products,
+      reactants: newReactionFixture.reactants,
+      zIndex: newReactionFixture.z,
+      pointToProjection: jest.fn(() => [10, 10]),
+      lineTypes: lineTypesFixture,
+      arrowTypes: arrowTypesFixture,
+    };
+  });
+
+  it('should initialize a Reaction class', () => {
+    const reaction = new Reaction(props);
+
+    expect(reaction.features.length).toBe(5);
+    expect(reaction.features).toBeInstanceOf(Array<Feature>);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b084d812ea3eb9f4c248cb3b1b2bff5347fbabf
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
@@ -0,0 +1,159 @@
+/* eslint-disable no-magic-numbers */
+import { ArrowType, Line, LineType, ReactionProduct } from '@/types/models';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import { Feature } from 'ol';
+import { LineString, MultiPolygon } from 'ol/geom';
+import getRotation from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation';
+import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
+import getArrowFeature from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature';
+
+export interface ReactionProps {
+  line: Line;
+  products: Array<ReactionProduct>;
+  reactants: Array<ReactionProduct>;
+  zIndex: number;
+  lineTypes: Array<LineType>;
+  arrowTypes: Array<ArrowType>;
+  pointToProjection: UsePointToProjectionResult;
+}
+
+export default class Reaction {
+  line: Line;
+
+  products: Array<ReactionProduct>;
+
+  reactants: Array<ReactionProduct>;
+
+  zIndex: number;
+
+  lineTypes: Array<LineType>;
+
+  arrowTypes: Array<ArrowType>;
+
+  pointToProjection: UsePointToProjectionResult;
+
+  features: Array<Feature> = [];
+
+  constructor({
+    line,
+    products,
+    reactants,
+    zIndex,
+    lineTypes,
+    arrowTypes,
+    pointToProjection,
+  }: ReactionProps) {
+    this.line = line;
+    this.products = products;
+    this.reactants = reactants;
+    this.zIndex = zIndex;
+    this.lineTypes = lineTypes;
+    this.arrowTypes = arrowTypes;
+    this.pointToProjection = pointToProjection;
+    let lineFeature = this.getLineFeature(this.line);
+    this.features.push(lineFeature.lineFeature);
+    this.features.push(...lineFeature.arrowsFeatures);
+    this.products.forEach(product => {
+      lineFeature = this.getLineFeature(product.line);
+      this.features.push(lineFeature.lineFeature);
+      this.features.push(...lineFeature.arrowsFeatures);
+    });
+    this.reactants.forEach(reactant => {
+      lineFeature = this.getLineFeature(reactant.line);
+      this.features.push(lineFeature.lineFeature);
+      this.features.push(...lineFeature.arrowsFeatures);
+    });
+  }
+
+  private getLineFeature = (
+    line: Line,
+  ): {
+    lineFeature: Feature<LineString>;
+    arrowsFeatures: Array<Feature<MultiPolygon>>;
+  } => {
+    const arrowsFeatures: Array<Feature<MultiPolygon>> = [];
+    const points = line.segments
+      .map((segment, index) => {
+        if (index === 0) {
+          return [
+            this.pointToProjection({ x: segment.x1, y: segment.y1 }),
+            this.pointToProjection({ x: segment.x2, y: segment.y2 }),
+          ];
+        }
+        return [this.pointToProjection({ x: segment.x2, y: segment.y2 })];
+      })
+      .flat();
+
+    if (line.startArrow.arrowType !== 'NONE') {
+      const firstSegment = line.segments[0];
+      let startArrowRotation = getRotation(
+        [firstSegment.x1, firstSegment.y1],
+        [firstSegment.x2, firstSegment.y2],
+      );
+      startArrowRotation += Math.PI;
+      const shortenedX1 = firstSegment.x1 - line.startArrow.length * Math.cos(startArrowRotation);
+      const shortenedY1 = firstSegment.y1 + line.startArrow.length * Math.sin(startArrowRotation);
+      points[0] = this.pointToProjection({ x: shortenedX1, y: shortenedY1 });
+      const startArrowFeature = getArrowFeature({
+        arrowTypes: this.arrowTypes,
+        arrow: line.startArrow,
+        x: shortenedX1,
+        y: shortenedY1,
+        zIndex: this.zIndex,
+        rotation: startArrowRotation,
+        lineWidth: line.width,
+        color: line.color,
+        pointToProjection: this.pointToProjection,
+      });
+      if (startArrowFeature) {
+        arrowsFeatures.push(startArrowFeature);
+      }
+    }
+
+    if (line.endArrow.arrowType !== 'NONE') {
+      const lastSegment = line.segments[line.segments.length - 1];
+      const endArrowRotation = getRotation(
+        [lastSegment.x1, lastSegment.y1],
+        [lastSegment.x2, lastSegment.y2],
+      );
+      const shortenedX2 = lastSegment.x2 - line.endArrow.length * Math.cos(endArrowRotation);
+      const shortenedY2 = lastSegment.y2 + line.endArrow.length * Math.sin(endArrowRotation);
+      points[points.length - 1] = this.pointToProjection({ x: shortenedX2, y: shortenedY2 });
+      const endArrowFeature = getArrowFeature({
+        arrowTypes: this.arrowTypes,
+        arrow: line.endArrow,
+        x: shortenedX2,
+        y: shortenedY2,
+        zIndex: this.zIndex,
+        rotation: endArrowRotation,
+        lineWidth: line.width,
+        color: line.color,
+        pointToProjection: this.pointToProjection,
+      });
+      if (endArrowFeature) {
+        arrowsFeatures.push(endArrowFeature);
+      }
+    }
+
+    const lineString = new LineString(points);
+
+    let lineDash;
+    const lineTypeFound = this.lineTypes.find(type => type.name === line.lineType);
+    if (lineTypeFound) {
+      lineDash = lineTypeFound.pattern;
+    }
+    const lineStyle = getStyle({
+      geometry: lineString,
+      borderColor: line.color,
+      lineWidth: line.width,
+      lineDash,
+      zIndex: this.zIndex,
+    });
+    const lineFeature = new Feature<LineString>({
+      geometry: lineString,
+    });
+    lineFeature.setStyle(lineStyle);
+
+    return { lineFeature, arrowsFeatures };
+  };
+}
diff --git a/src/models/fixtures/newReactionFixture.ts b/src/models/fixtures/newReactionFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e195b08c1fb50de0b37f8787a2452f7290a580a2
--- /dev/null
+++ b/src/models/fixtures/newReactionFixture.ts
@@ -0,0 +1,9 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { newReactionSchema } from '@/models/newReactionSchema';
+
+export const newReactionFixture = createFixture(newReactionSchema, {
+  seed: ZOD_SEED,
+  array: { min: 2, max: 2 },
+});
diff --git a/src/models/fixtures/newReactionsFixture.ts b/src/models/fixtures/newReactionsFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..88b3014ff37c5b78f068804aac8c6c0451a21eeb
--- /dev/null
+++ b/src/models/fixtures/newReactionsFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { pageableSchema } from '@/models/pageableSchema';
+import { newReactionSchema } from '@/models/newReactionSchema';
+
+export const newReactionsFixture = createFixture(pageableSchema(newReactionSchema), {
+  seed: ZOD_SEED,
+  array: { min: 2, max: 2 },
+});
diff --git a/src/models/fixtures/shapesFixture.ts b/src/models/fixtures/shapesFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b802d13e1185ed18f4b02c848d1c0372321cde81
--- /dev/null
+++ b/src/models/fixtures/shapesFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+import { z } from 'zod';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { shapeSchema } from '@/models/shapeSchema';
+
+export const shapesFixture = createFixture(z.array(shapeSchema), {
+  seed: ZOD_SEED,
+  array: { min: 2, max: 2 },
+});
diff --git a/src/models/newReactionSchema.ts b/src/models/newReactionSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..674f6a2a6920d25c3fd2bcd7b8be6a1a2b38a587
--- /dev/null
+++ b/src/models/newReactionSchema.ts
@@ -0,0 +1,34 @@
+import { z } from 'zod';
+import { lineSchema } from '@/models/lineSchema';
+import { reactionProduct } from '@/models/reactionProduct';
+import { referenceSchema } from '@/models/referenceSchema';
+
+export const newReactionSchema = z.object({
+  id: z.number(),
+  abbreviation: z.string().nullable(),
+  elementId: z.string(),
+  formula: z.string().nullable(),
+  geneProteinReaction: z.string().nullable(),
+  idReaction: z.string(),
+  kinetics: z.null(),
+  line: lineSchema,
+  lowerBound: z.number().nullable(),
+  mechanicalConfidenceScore: z.number().int().nullable(),
+  model: z.number(),
+  modifiers: z.array(reactionProduct),
+  name: z.string(),
+  notes: z.string(),
+  operators: z.array(z.unknown()),
+  processCoordinates: z.null(),
+  products: z.array(reactionProduct),
+  reactants: z.array(reactionProduct),
+  references: z.array(referenceSchema),
+  reversible: z.boolean(),
+  sboTerm: z.string(),
+  subsystem: z.null(),
+  symbol: z.null(),
+  synonyms: z.array(z.unknown()),
+  upperBound: z.null(),
+  visibilityLevel: z.string(),
+  z: z.number(),
+});
diff --git a/src/models/reactionProduct.ts b/src/models/reactionProduct.ts
index 96905877910e382f1736d595351286f5fa636807..7fe435ecc936ddbaa6adae51e37934d90e629853 100644
--- a/src/models/reactionProduct.ts
+++ b/src/models/reactionProduct.ts
@@ -4,6 +4,6 @@ import { lineSchema } from './lineSchema';
 export const reactionProduct = z.object({
   id: z.number(),
   line: lineSchema,
-  stoichiometry: z.null(),
+  stoichiometry: z.number().nullable(),
   element: z.number(),
 });
diff --git a/src/models/shapeEllipseSchema.ts b/src/models/shapeEllipseSchema.ts
index 920940192e207b79b9619ac8d461a07da5bc5bc7..843b653680714fa2e7e95b33d1df9828ea30445e 100644
--- a/src/models/shapeEllipseSchema.ts
+++ b/src/models/shapeEllipseSchema.ts
@@ -3,6 +3,7 @@ import { shapeRelAbsSchema } from '@/models/shapeRelAbsSchema';
 
 export const shapeEllipseSchema = z.object({
   type: z.literal('ELLIPSE'),
+  fill: z.boolean().nullable().optional(),
   center: shapeRelAbsSchema,
   radius: shapeRelAbsSchema,
 });
diff --git a/src/models/shapePolygonSchema.ts b/src/models/shapePolygonSchema.ts
index d7e2c59d8a2dc7d559c34d3ff6f8c4ce24df6dec..a3647cd1c9f6d7577c20f38ab04bde967829d87c 100644
--- a/src/models/shapePolygonSchema.ts
+++ b/src/models/shapePolygonSchema.ts
@@ -4,5 +4,6 @@ import { shapeRelAbsSchema } from '@/models/shapeRelAbsSchema';
 
 export const shapePolygonSchema = z.object({
   type: z.literal('POLYGON'),
+  fill: z.boolean().nullable().optional(),
   points: z.array(z.union([shapeRelAbsSchema, shapeRelAbsBezierPointSchema])),
 });
diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts
index 0c8b1125bf67d4a8ad2330eb24dec4d555cdae1a..784ec8427b0058270d8fc5cd3ac533e812fe680a 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -64,6 +64,8 @@ export const apiPath = {
     `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}/lines/`,
   getGlyphImage: (glyphId: number): string =>
     `projects/${PROJECT_ID}/glyphs/${glyphId}/fileContent`,
+  getNewReactions: (modelId: number): string =>
+    `projects/${PROJECT_ID}/maps/${modelId}/bioEntities/reactions/?size=2000`,
   getChemicalsStringWithQuery: (searchQuery: string): string =>
     `projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`,
   getAllOverlaysByProjectIdQuery: (
diff --git a/src/redux/newReactions/newReactions.constants.ts b/src/redux/newReactions/newReactions.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..80ea28ccf9f366c876a3dff5d6582db073fabe48
--- /dev/null
+++ b/src/redux/newReactions/newReactions.constants.ts
@@ -0,0 +1,10 @@
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
+
+export const NEW_REACTIONS_INITIAL_STATE: NewReactionsState = {
+  data: [],
+  loading: 'idle',
+  error: DEFAULT_ERROR,
+};
+
+export const NEW_REACTIONS_FETCHING_ERROR_PREFIX = 'Failed to fetch new reactions';
diff --git a/src/redux/newReactions/newReactions.mock.ts b/src/redux/newReactions/newReactions.mock.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3847aed65b5a817df00b05ba02e4a5a5c7e63133
--- /dev/null
+++ b/src/redux/newReactions/newReactions.mock.ts
@@ -0,0 +1,8 @@
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
+
+export const NEW_REACTIONS_INITIAL_STATE_MOCK: NewReactionsState = {
+  data: [],
+  loading: 'idle',
+  error: DEFAULT_ERROR,
+};
diff --git a/src/redux/newReactions/newReactions.reducers.test.ts b/src/redux/newReactions/newReactions.reducers.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21f8d6686e63ce18e3b924d3fa25f6f57e4c6687
--- /dev/null
+++ b/src/redux/newReactions/newReactions.reducers.test.ts
@@ -0,0 +1,79 @@
+/* eslint-disable no-magic-numbers */
+import { apiPath } from '@/redux/apiPath';
+import {
+  ToolkitStoreWithSingleSlice,
+  createStoreInstanceUsingSliceReducer,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import { unwrapResult } from '@reduxjs/toolkit';
+import newReactionsReducer from '@/redux/newReactions/newReactions.slice';
+import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
+import { NEW_REACTIONS_INITIAL_STATE_MOCK } from '@/redux/newReactions/newReactions.mock';
+import { getNewReactions } from '@/redux/newReactions/newReactions.thunks';
+import { newReactionsFixture } from '@/models/fixtures/newReactionsFixture';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+const INITIAL_STATE: NewReactionsState = NEW_REACTIONS_INITIAL_STATE_MOCK;
+
+describe('newReactions reducer', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<NewReactionsState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('newReactions', newReactionsReducer);
+  });
+
+  it('should match initial state', () => {
+    const action = { type: 'unknown' };
+
+    expect(newReactionsReducer(undefined, action)).toEqual(INITIAL_STATE);
+  });
+
+  it('should update store after successful getNewReactions query', async () => {
+    mockedAxiosClient
+      .onGet(apiPath.getNewReactions(1))
+      .reply(HttpStatusCode.Ok, newReactionsFixture);
+
+    const { type } = await store.dispatch(getNewReactions(1));
+    const { data, loading, error } = store.getState().newReactions;
+    expect(type).toBe('newReactions/getNewReactions/fulfilled');
+    expect(loading).toEqual('succeeded');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual(newReactionsFixture.content);
+  });
+
+  it('should update store after failed getNewReactions query', async () => {
+    mockedAxiosClient.onGet(apiPath.getNewReactions(1)).reply(HttpStatusCode.NotFound, []);
+
+    const action = await store.dispatch(getNewReactions(1));
+    const { data, loading, error } = store.getState().newReactions;
+
+    expect(action.type).toBe('newReactions/getNewReactions/rejected');
+    expect(() => unwrapResult(action)).toThrow(
+      "Failed to fetch new reactions: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
+    expect(loading).toEqual('failed');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual([]);
+  });
+
+  it('should update store on loading getNewReactions query', async () => {
+    mockedAxiosClient
+      .onGet(apiPath.getNewReactions(1))
+      .reply(HttpStatusCode.Ok, newReactionsFixture);
+
+    const newReactionsPromise = store.dispatch(getNewReactions(1));
+
+    const { data, loading } = store.getState().newReactions;
+    expect(data).toEqual([]);
+    expect(loading).toEqual('pending');
+
+    newReactionsPromise.then(() => {
+      const { data: dataPromiseFulfilled, loading: promiseFulfilled } =
+        store.getState().newReactions;
+
+      expect(dataPromiseFulfilled).toEqual(newReactionsFixture.content);
+      expect(promiseFulfilled).toEqual('succeeded');
+    });
+  });
+});
diff --git a/src/redux/newReactions/newReactions.reducers.ts b/src/redux/newReactions/newReactions.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..306b306e770cfb0097e256e1aed05fa8017d2990
--- /dev/null
+++ b/src/redux/newReactions/newReactions.reducers.ts
@@ -0,0 +1,18 @@
+import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+import { getNewReactions } from '@/redux/newReactions/newReactions.thunks';
+import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
+
+export const getNewReactionsReducer = (
+  builder: ActionReducerMapBuilder<NewReactionsState>,
+): void => {
+  builder.addCase(getNewReactions.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getNewReactions.fulfilled, (state, action) => {
+    state.data = action.payload || [];
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getNewReactions.rejected, state => {
+    state.loading = 'failed';
+  });
+};
diff --git a/src/redux/newReactions/newReactions.selectors.ts b/src/redux/newReactions/newReactions.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4dc2babe517c9850b03cf090644ebe2389550dcd
--- /dev/null
+++ b/src/redux/newReactions/newReactions.selectors.ts
@@ -0,0 +1,9 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { rootSelector } from '../root/root.selectors';
+
+export const newReactionsSelector = createSelector(rootSelector, state => state.newReactions);
+
+export const newReactionsDataSelector = createSelector(
+  newReactionsSelector,
+  reactions => reactions.data || [],
+);
diff --git a/src/redux/newReactions/newReactions.slice.ts b/src/redux/newReactions/newReactions.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5bb5198a1f58d89036338741124f555a64c9b71a
--- /dev/null
+++ b/src/redux/newReactions/newReactions.slice.ts
@@ -0,0 +1,14 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { NEW_REACTIONS_INITIAL_STATE } from '@/redux/newReactions/newReactions.constants';
+import { getNewReactionsReducer } from '@/redux/newReactions/newReactions.reducers';
+
+export const newReactionsSlice = createSlice({
+  name: 'reactions',
+  initialState: NEW_REACTIONS_INITIAL_STATE,
+  reducers: {},
+  extraReducers: builder => {
+    getNewReactionsReducer(builder);
+  },
+});
+
+export default newReactionsSlice.reducer;
diff --git a/src/redux/newReactions/newReactions.thunks.test.ts b/src/redux/newReactions/newReactions.thunks.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..afd14d77104b12dcbfaa00d460ca2221244947b9
--- /dev/null
+++ b/src/redux/newReactions/newReactions.thunks.test.ts
@@ -0,0 +1,41 @@
+/* eslint-disable no-magic-numbers */
+import { apiPath } from '@/redux/apiPath';
+import {
+  ToolkitStoreWithSingleSlice,
+  createStoreInstanceUsingSliceReducer,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import newReactionsReducer from '@/redux/newReactions/newReactions.slice';
+import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
+import { newReactionsFixture } from '@/models/fixtures/newReactionsFixture';
+import { getNewReactions } from '@/redux/newReactions/newReactions.thunks';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+describe('newReactions thunks', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<NewReactionsState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('newReactions', newReactionsReducer);
+  });
+
+  describe('getReactions', () => {
+    it('should return data when data response from API is valid', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getNewReactions(1))
+        .reply(HttpStatusCode.Ok, newReactionsFixture);
+
+      const { payload } = await store.dispatch(getNewReactions(1));
+      expect(payload).toEqual(newReactionsFixture.content);
+    });
+
+    it('should return undefined when data response from API is not valid ', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getNewReactions(1))
+        .reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
+
+      const { payload } = await store.dispatch(getNewReactions(1));
+      expect(payload).toEqual(undefined);
+    });
+  });
+});
diff --git a/src/redux/newReactions/newReactions.thunks.ts b/src/redux/newReactions/newReactions.thunks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7e7081a85787f62592bce8fc9095714a45eef4b3
--- /dev/null
+++ b/src/redux/newReactions/newReactions.thunks.ts
@@ -0,0 +1,24 @@
+import { apiPath } from '@/redux/apiPath';
+import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
+import { NewReaction, NewReactions } from '@/types/models';
+import { ThunkConfig } from '@/types/store';
+import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
+import { createAsyncThunk } from '@reduxjs/toolkit';
+import { getError } from '@/utils/error-report/getError';
+import { newReactionSchema } from '@/models/newReactionSchema';
+import { pageableSchema } from '@/models/pageableSchema';
+import { NEW_REACTIONS_FETCHING_ERROR_PREFIX } from '@/redux/newReactions/newReactions.constants';
+
+export const getNewReactions = createAsyncThunk<
+  Array<NewReaction> | undefined,
+  number,
+  ThunkConfig
+>('newReactions/getNewReactions', async (modelId: number) => {
+  try {
+    const { data } = await axiosInstanceNewAPI.get<NewReactions>(apiPath.getNewReactions(modelId));
+    const isDataValid = validateDataUsingZodSchema(data, pageableSchema(newReactionSchema));
+    return isDataValid ? data.content : undefined;
+  } catch (error) {
+    return Promise.reject(getError({ error, prefix: NEW_REACTIONS_FETCHING_ERROR_PREFIX }));
+  }
+});
diff --git a/src/redux/newReactions/newReactions.types.ts b/src/redux/newReactions/newReactions.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..142906d7f46037ae9789eee1e74504ea2408d55f
--- /dev/null
+++ b/src/redux/newReactions/newReactions.types.ts
@@ -0,0 +1,4 @@
+import { FetchDataState } from '@/types/fetchDataState';
+import { NewReaction } from '@/types/models';
+
+export type NewReactionsState = FetchDataState<NewReaction[]>;
diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts
index 8b1cec49d6e6fe6104c2487e898f080f322e2a1a..007c2b355b0bb4cc0dbf4a5d5e0a9d84b7b8d19c 100644
--- a/src/redux/root/root.fixtures.ts
+++ b/src/redux/root/root.fixtures.ts
@@ -6,6 +6,7 @@ import { AUTOCOMPLETE_INITIAL_STATE } from '@/redux/autocomplete/autocomplete.co
 import { SHAPES_STATE_INITIAL_MOCK } from '@/redux/shapes/shapes.mock';
 import { MODEL_ELEMENTS_INITIAL_STATE_MOCK } from '@/redux/modelElements/modelElements.mock';
 import { LAYERS_STATE_INITIAL_MOCK } from '@/redux/layers/layers.mock';
+import { NEW_REACTIONS_INITIAL_STATE_MOCK } from '@/redux/newReactions/newReactions.mock';
 import { BACKGROUND_INITIAL_STATE_MOCK } from '../backgrounds/background.mock';
 import { BIOENTITY_INITIAL_STATE_MOCK } from '../bioEntity/bioEntity.mock';
 import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock';
@@ -53,6 +54,7 @@ export const INITIAL_STORE_STATE_MOCK: RootState = {
   oauth: OAUTH_INITIAL_STATE_MOCK,
   overlays: OVERLAYS_INITIAL_STATE_MOCK,
   reactions: REACTIONS_STATE_INITIAL_MOCK,
+  newReactions: NEW_REACTIONS_INITIAL_STATE_MOCK,
   configuration: CONFIGURATION_INITIAL_STATE,
   constant: CONSTANT_INITIAL_STATE,
   overlayBioEntity: OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK,
diff --git a/src/redux/store.ts b/src/redux/store.ts
index f6b8ad65c8648756943c421cc42fd796f75e6bd7..a5d31bb57d0a4db6a0bae569a15a89579317c211 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -19,6 +19,7 @@ import overlaysReducer from '@/redux/overlays/overlays.slice';
 import projectReducer from '@/redux/project/project.slice';
 import projectsReducer from '@/redux/projects/projects.slice';
 import reactionsReducer from '@/redux/reactions/reactions.slice';
+import newReactionsReducer from '@/redux/newReactions/newReactions.slice';
 import searchReducer from '@/redux/search/search.slice';
 import userReducer from '@/redux/user/user.slice';
 import {
@@ -66,6 +67,7 @@ export const reducers = {
   modelElements: modelElementsReducer,
   layers: layersReducer,
   reactions: reactionsReducer,
+  newReactions: newReactionsReducer,
   contextMenu: contextMenuReducer,
   cookieBanner: cookieBannerReducer,
   user: userReducer,
diff --git a/src/types/models.ts b/src/types/models.ts
index 1b09b1d9d20308b26566400d6d29efdb3698e9c1..1e6ac2bb90801824bc5b8f1da37e20b71e2cf98d 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -80,6 +80,8 @@ import { arrowTypeSchema } from '@/models/arrowTypeSchema';
 import { arrowSchema } from '@/models/arrowSchema';
 import { shapeRelAbsSchema } from '@/models/shapeRelAbsSchema';
 import { shapeRelAbsBezierPointSchema } from '@/models/shapeRelAbsBezierPointSchema';
+import { newReactionSchema } from '@/models/newReactionSchema';
+import { reactionProduct } from '@/models/reactionProduct';
 
 export type Project = z.infer<typeof projectSchema>;
 export type OverviewImageView = z.infer<typeof overviewImageView>;
@@ -116,6 +118,10 @@ export type BioEntityContent = z.infer<typeof bioEntityContentSchema>;
 export type BioEntityResponse = z.infer<typeof bioEntityResponseSchema>;
 export type Chemical = z.infer<typeof chemicalSchema>;
 export type Reaction = z.infer<typeof reactionSchema>;
+export type NewReaction = z.infer<typeof newReactionSchema>;
+const newReactionsSchema = pageableSchema(newReactionSchema);
+export type NewReactions = z.infer<typeof newReactionsSchema>;
+export type ReactionProduct = z.infer<typeof reactionProduct>;
 export type Reference = z.infer<typeof referenceSchema>;
 export type ReactionLine = z.infer<typeof reactionLineSchema>;
 export type ElementSearchResult = z.infer<typeof elementSearchResult>;