diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.test.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d44c61bad6dba6ac0b6ad38e5b67dabaeb9f7c83
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.test.tsx
@@ -0,0 +1,140 @@
+/* eslint-disable react/no-children-prop */
+import { FIRST_ARRAY_ELEMENT, SECOND_ARRAY_ELEMENT, THIRD_ARRAY_ELEMENT } from '@/constants/common';
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
+import { MODELS_MOCK } from '@/models/mocks/modelsMock';
+import { PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK } from '@/models/mocks/publicationsResponseMock';
+import { apiPath } from '@/redux/apiPath';
+import { downloadFileFromBlob } from '@/redux/export/export.utils';
+import { StoreType } from '@/redux/store';
+import { BioEntityContent, Publication } from '@/types/models';
+import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import {
+  InitialStoreState,
+  getReduxWrapperWithStore,
+} from '@/utils/testing/getReduxWrapperWithStore';
+import { render, screen, waitFor } from '@testing-library/react';
+import { HttpStatusCode } from 'axios';
+import { fetchElementData } from '../utils/fetchElementData';
+import { PublicationsModalLayout } from './PublicationsModalLayout.component';
+
+const FIRST_MODEL_ID = MODELS_MOCK[FIRST_ARRAY_ELEMENT].idObject;
+const SECOND_MODEL_ID = MODELS_MOCK[SECOND_ARRAY_ELEMENT].idObject;
+const THIRD_MODEL_ID = MODELS_MOCK[THIRD_ARRAY_ELEMENT].idObject;
+
+const FIRST_ELEMENT_ID = 100;
+const SECOND_ELEMENT_ID = 200;
+const THIRD_ELEMENT_ID = 300;
+const FOURTH_ELEMENT_ID = 400;
+
+const BASE_PUBLICATION: Publication =
+  PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK.data[FIRST_ARRAY_ELEMENT];
+const BASE_ELEMENT = BASE_PUBLICATION.elements[FIRST_ARRAY_ELEMENT];
+const PUBLICATION: Publication = {
+  ...BASE_PUBLICATION,
+  elements: [
+    {
+      ...BASE_ELEMENT,
+      id: FIRST_ELEMENT_ID,
+      modelId: FIRST_MODEL_ID,
+    },
+    {
+      ...BASE_ELEMENT,
+      id: SECOND_ELEMENT_ID,
+      modelId: SECOND_MODEL_ID,
+    },
+    {
+      ...BASE_ELEMENT,
+      id: THIRD_ELEMENT_ID,
+      modelId: THIRD_MODEL_ID, // model id duplicate
+    },
+    {
+      ...BASE_ELEMENT,
+      id: FOURTH_ELEMENT_ID,
+      modelId: THIRD_MODEL_ID, // model id duplicate
+    },
+  ],
+};
+
+const BIO_ENTITY_CONTENT = (id: number, elementId: string): BioEntityContent => ({
+  ...bioEntityContentFixture,
+  bioEntity: {
+    ...bioEntityContentFixture.bioEntity,
+    id,
+    elementId,
+  },
+});
+
+jest.mock('../../../../../redux/export/export.utils');
+jest.mock('../utils/fetchElementData');
+(fetchElementData as jest.Mock).mockImplementation(
+  (id: string): BioEntityContent =>
+    ({
+      [`${FIRST_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(FIRST_ELEMENT_ID, 'mi100'),
+      [`${SECOND_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(SECOND_ELEMENT_ID, 'ne200'),
+      [`${THIRD_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(THIRD_ELEMENT_ID, 'r300'),
+      [`${FOURTH_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(FOURTH_ELEMENT_ID, 'va400'),
+    })[id] as BioEntityContent,
+);
+
+const mockedAxiosClient = mockNetworkResponse();
+
+const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore(initialStore);
+
+  return (
+    render(
+      <Wrapper>
+        <PublicationsModalLayout children={null} />
+      </Wrapper>,
+    ),
+    {
+      store,
+    }
+  );
+};
+
+describe('PublicationsModalLayout - component', () => {
+  const length = 1;
+  mockedAxiosClient
+    .onGet(apiPath.getPublications({ params: { length } }))
+    .reply(HttpStatusCode.Ok, {
+      ...PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK,
+      data: [PUBLICATION],
+    });
+
+  it('should render download csv button', () => {
+    renderComponent();
+
+    expect(screen.getByTestId('download-csv-button')).toBeInTheDocument();
+  });
+
+  it('should run download file with valid content when download csv clicked', async () => {
+    renderComponent({
+      publications: {
+        data: {
+          ...PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK,
+          data: [PUBLICATION],
+          filteredSize: length,
+        },
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        sortColumn: '',
+        sortOrder: 'asc',
+        searchValue: '',
+        selectedModelId: undefined,
+      },
+      models: { data: MODELS_MOCK, loading: 'idle', error: DEFAULT_ERROR },
+    });
+
+    const downloadButton = screen.getByTestId('download-csv-button');
+    downloadButton.click();
+
+    await waitFor(() => {
+      expect(downloadFileFromBlob).toHaveBeenCalledWith(
+        '"10049997","The glutamate receptor ion channels.","Dingledine R, Borges K, Bowie D, Traynelis SF.","Pharmacological reviews","1999","mi100,ne200,r300,va400","Core PD map,Histamine signaling,PRKN substrates"',
+        'publications.csv',
+      );
+    });
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.tsx
index ba67f423b7af32dcacd1f7969bb2d756a624f926..0b5536b0953cbea2aa434bcf0bc8d86f9fb91abc 100644
--- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.tsx
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.tsx
@@ -1,19 +1,24 @@
-import { twMerge } from 'tailwind-merge';
+import spinnerIcon from '@/assets/vectors/icons/spinner.svg';
 import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { closeModal } from '@/redux/modal/modal.slice';
-import { Icon } from '@/shared/Icon';
 import { filteredSizeSelector } from '@/redux/publications/publications.selectors';
-import { MODAL_ROLE } from './PublicationsModalLayout.constants';
+import { Button } from '@/shared/Button';
+import { Icon } from '@/shared/Icon';
+import Image from 'next/image';
+import { twMerge } from 'tailwind-merge';
 import { PublicationsSearch } from '../PublicationsSearch';
+import { MODAL_ROLE } from './PublicationsModalLayout.constants';
+import { useDownloadPublicationsAsCSVFile } from './utils/useDownloadPublicationsAsCSVFile';
 
 type ModalLayoutProps = {
-  children: React.ReactNode;
+  children: React.ReactNode | null;
 };
 
 export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Element => {
   const dispatch = useAppDispatch();
   const numberOfPublications = useAppSelector(filteredSizeSelector);
+  const { downloadPublicationsAsCSVFile, isLoading } = useDownloadPublicationsAsCSVFile();
 
   const handleCloseModal = (): void => {
     dispatch(closeModal());
@@ -27,8 +32,26 @@ export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Ele
       <div className="flex h-full w-full items-center justify-center">
         <div className={twMerge('flex h-5/6 w-10/12	flex-col	overflow-hidden rounded-lg')}>
           <div className="flex items-center  justify-between bg-white p-[24px] text-xl">
-            <div className="font-semibold">
-              <div>Publications ({numberOfPublications} results)</div>
+            <div className="flex items-center gap-4">
+              <div className="font-semibold">
+                <div>Publications ({numberOfPublications} results)</div>
+              </div>
+              <Button
+                onClick={downloadPublicationsAsCSVFile}
+                disabled={isLoading || !numberOfPublications}
+                data-testid="download-csv-button"
+              >
+                {isLoading && (
+                  <Image
+                    src={spinnerIcon}
+                    alt="spinner icon"
+                    height={12}
+                    width={12}
+                    className="animate-spin"
+                  />
+                )}
+                Download CSV
+              </Button>
             </div>
             <div className="flex flex-row flex-nowrap items-center">
               <PublicationsSearch />
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb9a4b10c26bcb879b2e9066173d1e728f355e1c
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts
@@ -0,0 +1,49 @@
+import { apiPath } from '@/redux/apiPath';
+import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import { PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK } from '../../../../../../models/mocks/publicationsResponseMock';
+import { showToast } from '../../../../../../utils/showToast';
+import { getBasePublications } from './getBasePublications';
+
+const mockedAxiosClient = mockNetworkResponse();
+
+jest.mock('./../../../../../../utils/showToast');
+
+describe('getBasePublications - util', () => {
+  const length = 10;
+
+  it('should return valid data if provided', async () => {
+    mockedAxiosClient
+      .onGet(apiPath.getPublications({ params: { length } }))
+      .reply(HttpStatusCode.Ok, PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK);
+
+    const result = await getBasePublications({ length });
+
+    expect(result).toStrictEqual(PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK.data);
+  });
+
+  it('should return empty array if data structure is invalid', async () => {
+    mockedAxiosClient
+      .onGet(apiPath.getPublications({ params: { length } }))
+      .reply(HttpStatusCode.Ok, { randomObject: true });
+
+    const result = await getBasePublications({ length });
+
+    expect(result).toStrictEqual([]);
+  });
+
+  it('should return empty array and show toast error if http error', async () => {
+    mockedAxiosClient
+      .onGet(apiPath.getPublications({ params: { length } }))
+      .reply(HttpStatusCode.BadRequest);
+
+    const result = await getBasePublications({ length });
+
+    expect(result).toStrictEqual([]);
+    expect(showToast).toHaveBeenCalledWith({
+      message:
+        "Problem with fetching publications: The server couldn't understand your request. Please check your input and try again.",
+      type: 'error',
+    });
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7725df112b3f7d8f4d5464a52ba0caee6e29557c
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts
@@ -0,0 +1,32 @@
+import { publicationsResponseSchema } from '@/models/publicationsResponseSchema';
+import { apiPath } from '@/redux/apiPath';
+import { PUBLICATIONS_FETCHING_ERROR_PREFIX } from '@/redux/publications/publications.constatns';
+import { axiosInstance } from '@/services/api/utils/axiosInstance';
+import { Publication, PublicationsResponse } from '@/types/models';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { showToast } from '@/utils/showToast';
+import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
+
+interface Args {
+  length: number;
+}
+
+export const getBasePublications = async ({ length }: Args): Promise<Publication[]> => {
+  try {
+    const response = await axiosInstance.get<PublicationsResponse>(
+      apiPath.getPublications({ params: { length } }),
+    );
+
+    const isDataValid = validateDataUsingZodSchema(response.data, publicationsResponseSchema);
+
+    return isDataValid ? response.data.data : [];
+  } catch (error) {
+    const errorMessage = getErrorMessage({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX });
+    showToast({
+      type: 'error',
+      message: errorMessage,
+    });
+
+    return [];
+  }
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getStandarizedPublications.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getStandarizedPublications.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c59d2e00d5b823e2cf9ff9ecd1b072ea00f85f87
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getStandarizedPublications.ts
@@ -0,0 +1,30 @@
+import { Publication } from '@/types/models';
+import { StandarizedPublication } from '@/types/publications';
+import { runInSequence } from '@/utils/promise/runInSequence';
+import { mapBasePublicationToStandarized } from './mapBasePublicationToStandarized';
+
+interface Args {
+  modelNameIdMap: Record<number, string>;
+  publications: Publication[];
+}
+
+const SEQUENCE_CHUNK_SIZE = 250;
+
+export const getStandarizedPublications = async ({
+  publications,
+  modelNameIdMap,
+}: Args): Promise<StandarizedPublication[]> => {
+  const getStandarizedPublicationsFuncs = publications.map(
+    publication => () =>
+      mapBasePublicationToStandarized(publication, {
+        modelNameIdMap,
+      }),
+  );
+
+  const standarizedPublications = await runInSequence<StandarizedPublication>(
+    getStandarizedPublicationsFuncs,
+    SEQUENCE_CHUNK_SIZE,
+  );
+
+  return standarizedPublications;
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapBasePublicationToStandarized.test.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapBasePublicationToStandarized.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a556efff2d648e8d1f548b4ee944d323d4e09aa
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapBasePublicationToStandarized.test.ts
@@ -0,0 +1,116 @@
+/* eslint-disable no-magic-numbers */
+import { FIRST_ARRAY_ELEMENT } from '@/constants/common';
+import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
+import { PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK } from '@/models/mocks/publicationsResponseMock';
+import { BioEntityContent, Publication } from '@/types/models';
+import { StandarizedPublication } from '@/types/publications';
+import { fetchElementData } from '../../utils/fetchElementData';
+import { mapBasePublicationToStandarized } from './mapBasePublicationToStandarized';
+
+const FIRST_MODEL_ID = 53;
+const SECOND_MODEL_ID = 63;
+const THIRD_MODEL_ID = 99;
+
+const FIRST_ELEMENT_ID = 100;
+const SECOND_ELEMENT_ID = 200;
+const THIRD_ELEMENT_ID = 300;
+const FOURTH_ELEMENT_ID = 400;
+
+const MODEL_NAME_ID_MAP: Record<number, string> = {
+  [FIRST_MODEL_ID]: 'first model',
+  [SECOND_MODEL_ID]: 'second model',
+  [THIRD_MODEL_ID]: 'third model',
+};
+
+const BASE_PUBLICATION: Publication =
+  PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK.data[FIRST_ARRAY_ELEMENT];
+const BASE_ELEMENT = BASE_PUBLICATION.elements[FIRST_ARRAY_ELEMENT];
+const PUBLICATION: Publication = {
+  ...BASE_PUBLICATION,
+  elements: [
+    {
+      ...BASE_ELEMENT,
+      id: FIRST_ELEMENT_ID,
+      modelId: FIRST_MODEL_ID,
+    },
+    {
+      ...BASE_ELEMENT,
+      id: SECOND_ELEMENT_ID,
+      modelId: SECOND_MODEL_ID,
+    },
+    {
+      ...BASE_ELEMENT,
+      id: THIRD_ELEMENT_ID,
+      modelId: THIRD_MODEL_ID, // model id duplicate
+    },
+    {
+      ...BASE_ELEMENT,
+      id: FOURTH_ELEMENT_ID,
+      modelId: THIRD_MODEL_ID, // model id duplicate
+    },
+  ],
+};
+
+const BIO_ENTITY_CONTENT = (id: number, elementId: string): BioEntityContent => ({
+  ...bioEntityContentFixture,
+  bioEntity: {
+    ...bioEntityContentFixture.bioEntity,
+    id,
+    elementId,
+  },
+});
+
+jest.mock('../../utils/fetchElementData');
+(fetchElementData as jest.Mock).mockImplementation(
+  (id: string): BioEntityContent =>
+    ({
+      [`${FIRST_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(FIRST_ELEMENT_ID, 'mi100'),
+      [`${SECOND_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(SECOND_ELEMENT_ID, 'ne200'),
+      [`${THIRD_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(THIRD_ELEMENT_ID, 'r300'),
+      [`${FOURTH_ELEMENT_ID}`]: BIO_ENTITY_CONTENT(FOURTH_ELEMENT_ID, 'va400'),
+    })[id] as BioEntityContent,
+);
+
+const getFuncResult = async (
+  publication: Publication,
+  modelNameIdMap: Record<number, string> = MODEL_NAME_ID_MAP,
+): Promise<StandarizedPublication> =>
+  mapBasePublicationToStandarized(publication, { modelNameIdMap });
+
+describe('mapBasePublicationToStandarized - util', () => {
+  it('should return valid pubmedId, journal, year and title', async () => {
+    const results = await getFuncResult(PUBLICATION);
+    const { pubmedId, journal, year, title } = PUBLICATION.publication.article;
+
+    expect(results.pubmedId).toBe(pubmedId);
+    expect(results.journal).toBe(journal);
+    expect(results.year).toBe(year.toString());
+    expect(results.title).toBe(title);
+  });
+
+  it('should return joined authors', async () => {
+    const results = await getFuncResult(PUBLICATION);
+    const { authors } = PUBLICATION.publication.article;
+
+    expect(results.authors).toBe(authors.join(','));
+  });
+
+  it('should return joined and unique model names if present', async () => {
+    const results = await getFuncResult(PUBLICATION);
+
+    expect(results.modelNames).toBe(['first model', 'second model', 'third model'].join(','));
+  });
+
+  it('should return empty model names string if model not existing', async () => {
+    const EMPTY_MODEL_ID_MAP = {};
+    const results = await getFuncResult(PUBLICATION, EMPTY_MODEL_ID_MAP);
+
+    expect(results.modelNames).toBe('');
+  });
+
+  it('should return joined elementsIds', async () => {
+    const results = await getFuncResult(PUBLICATION);
+
+    expect(results.elementsIds).toBe(['mi100', 'ne200', 'r300', 'va400'].join(','));
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapBasePublicationToStandarized.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapBasePublicationToStandarized.ts
new file mode 100644
index 0000000000000000000000000000000000000000..84b7d269b8259c2e7140aa96a555f42ab897e6b5
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapBasePublicationToStandarized.ts
@@ -0,0 +1,37 @@
+import { BioEntityContent, Publication } from '@/types/models';
+import { StandarizedPublication } from '@/types/publications';
+import { getUniqueArray } from '@/utils/array/getUniqueArray';
+import { fetchElementData } from '../../utils/fetchElementData';
+
+interface Options {
+  modelNameIdMap: Record<number, string>;
+}
+
+const JOIN_SEPARATOR = ',';
+
+export const mapBasePublicationToStandarized = async (
+  publication: Publication,
+  options: Options,
+): Promise<StandarizedPublication> => {
+  const {
+    publication: { article },
+    elements,
+  } = publication;
+  const { modelNameIdMap } = options;
+  const { title, authors, journal, year, pubmedId } = article;
+  const modelNames = elements.map(({ modelId }) => modelNameIdMap[modelId]);
+  const elementsData = await Promise.all(elements.map(async ({ id }) => fetchElementData(`${id}`)));
+  const elementsIds = elementsData
+    .filter((element): element is BioEntityContent => element !== undefined)
+    .map(({ bioEntity }) => bioEntity.elementId);
+
+  return {
+    pubmedId,
+    journal,
+    title,
+    year: year ? `${year}` : '',
+    authors: authors.join(JOIN_SEPARATOR),
+    modelNames: getUniqueArray(modelNames).join(JOIN_SEPARATOR),
+    elementsIds: elementsIds.join(JOIN_SEPARATOR),
+  };
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapStandarizedPublicationsToCSVString.test.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapStandarizedPublicationsToCSVString.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..07588cfd0f413c303a87ae13ca9e395f91c76348
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapStandarizedPublicationsToCSVString.test.ts
@@ -0,0 +1,40 @@
+import { StandarizedPublication } from '@/types/publications';
+import { mapStandarizedPublicationsToCSVString } from './mapStandarizedPublicationsToCSVString';
+
+const CASES: [StandarizedPublication[], string][] = [
+  [[], ''],
+  [
+    [
+      {
+        pubmedId: '',
+        year: '',
+        journal: '',
+        authors: '',
+        title: '',
+        modelNames: '',
+        elementsIds: '',
+      },
+    ],
+    '"","","","","","",""',
+  ],
+  [
+    [
+      {
+        authors: 'authors',
+        title: 'title',
+        journal: 'journal',
+        pubmedId: 'pubmedId',
+        modelNames: 'modelNames',
+        elementsIds: 'elementsIds',
+        year: 'year',
+      },
+    ],
+    '"pubmedId","title","authors","journal","year","elementsIds","modelNames"',
+  ],
+];
+
+describe('mapStandarizedPublicationsToCSVString - util', () => {
+  it.each(CASES)('should return valid string', (input, result) => {
+    expect(mapStandarizedPublicationsToCSVString(input)).toBe(result);
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapStandarizedPublicationsToCSVString.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapStandarizedPublicationsToCSVString.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d24ff87e7bcce7a1c18a8ce991fc6556ab74f27b
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/mapStandarizedPublicationsToCSVString.ts
@@ -0,0 +1,12 @@
+import { StandarizedPublication } from '@/types/publications';
+
+export const mapStandarizedPublicationsToCSVString = (
+  publications: StandarizedPublication[],
+): string =>
+  publications
+    .map(({ pubmedId, title, authors, journal, year, elementsIds, modelNames }) =>
+      [pubmedId, title, authors, journal, year, elementsIds, modelNames]
+        .map(text => `"${text}"`)
+        .join(','),
+    )
+    .join('\n');
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/useDownloadPublicationsAsCSVFile.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/useDownloadPublicationsAsCSVFile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..531faf9c6429071231383a9fbf0731ea753774ee
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/useDownloadPublicationsAsCSVFile.ts
@@ -0,0 +1,65 @@
+import { ZERO } from '@/constants/common';
+import { downloadFileFromBlob } from '@/redux/export/export.utils';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { modelsNameMapSelector } from '@/redux/models/models.selectors';
+import {
+  filteredSizeSelector,
+  publicationsListDataSelector,
+  searchValueSelector,
+} from '@/redux/publications/publications.selectors';
+import { Publication } from '@/types/models';
+import { StandarizedPublication } from '@/types/publications';
+import { useState } from 'react';
+import { getBasePublications } from './getBasePublications';
+import { getStandarizedPublications } from './getStandarizedPublications';
+import { mapStandarizedPublicationsToCSVString } from './mapStandarizedPublicationsToCSVString';
+
+export type DownloadPublicationsAsCSVFile = () => Promise<void>;
+
+interface UseDownloadPublicationsAsCSVFileResult {
+  downloadPublicationsAsCSVFile: DownloadPublicationsAsCSVFile;
+  isLoading: boolean;
+}
+
+const CSV_FILE_NAME = 'publications.csv';
+
+export const useDownloadPublicationsAsCSVFile = (): UseDownloadPublicationsAsCSVFileResult => {
+  const [isLoading, setIsLoading] = useState<boolean>(false);
+  const modelNameIdMap = useAppSelector(modelsNameMapSelector);
+  const numberOfPublications = useAppSelector(filteredSizeSelector);
+  const searchValue = useAppSelector(searchValueSelector);
+  const searchedPublicationsList = useAppSelector(publicationsListDataSelector);
+
+  const getAllBasePublications = async (): Promise<Publication[]> =>
+    getBasePublications({
+      length: numberOfPublications || ZERO,
+    });
+
+  const getPublicationsAsList = async (): Promise<StandarizedPublication[]> => {
+    const publications =
+      searchValue && searchedPublicationsList
+        ? searchedPublicationsList
+        : await getAllBasePublications();
+
+    const standarizedPublications = await getStandarizedPublications({
+      publications,
+      modelNameIdMap,
+    });
+
+    return standarizedPublications;
+  };
+
+  const downloadPublicationsAsCSVFile = async (): Promise<void> => {
+    setIsLoading(true);
+    const data = await getPublicationsAsList();
+    const dataString = mapStandarizedPublicationsToCSVString(data);
+
+    downloadFileFromBlob(dataString, CSV_FILE_NAME);
+    setIsLoading(false);
+  };
+
+  return {
+    downloadPublicationsAsCSVFile,
+    isLoading,
+  };
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/ElementLink.component.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/ElementLink.component.tsx
index efb424f3b36df1861110b9a3e26f576f3e4e6973..a3f28f15b9c79b302864b3572a8ce16024a9ba56 100644
--- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/ElementLink.component.tsx
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/ElementLink.component.tsx
@@ -16,7 +16,7 @@ import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
 import { LoadingIndicator } from '@/shared/LoadingIndicator';
 import { BioEntityContent, TargetElement } from '@/types/models';
 import { useEffect, useState } from 'react';
-import { fetchElementLinkData } from './utils/fetchElementLinkData';
+import { fetchElementData } from '../../../utils/fetchElementData';
 
 interface Props {
   target: TargetElement;
@@ -35,7 +35,7 @@ export const ElementLink = ({ target }: Props): JSX.Element => {
     openedMaps.some(map => map.modelId === modelId);
 
   const getElementLinkData = async (searchQuery: string): Promise<void> => {
-    const fetchedData = await fetchElementLinkData(searchQuery).finally(() => setIsLoading(false));
+    const fetchedData = await fetchElementData(searchQuery).finally(() => setIsLoading(false));
 
     if (fetchedData) {
       setData(fetchedData);
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/utils/fetchElementLinkData.ts b/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts
similarity index 96%
rename from src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/utils/fetchElementLinkData.ts
rename to src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts
index a4d1ed80ab54399adebfb0a8e0ee720b03105df1..ceaf1b8a355e43d7c29c4c54eb8635c1f1f3233c 100644
--- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/utils/fetchElementLinkData.ts
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts
@@ -8,7 +8,7 @@ import { getErrorMessage } from '@/utils/getErrorMessage';
 import { showToast } from '@/utils/showToast';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 
-export const fetchElementLinkData = async (
+export const fetchElementData = async (
   searchQuery: string,
 ): Promise<BioEntityContent | undefined> => {
   try {
diff --git a/src/models/mocks/publicationsResponseMock.ts b/src/models/mocks/publicationsResponseMock.ts
index 09021f4ed644552f37e1fa98ccbd0240f424ee00..c165eb985d4ca669ba36272d8d65ed3935c947c8 100644
--- a/src/models/mocks/publicationsResponseMock.ts
+++ b/src/models/mocks/publicationsResponseMock.ts
@@ -1,4 +1,6 @@
-export const PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK = {
+import { PublicationsResponse } from '@/types/models';
+
+export const PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK: PublicationsResponse = {
   data: [
     {
       elements: [
diff --git a/src/types/publications.ts b/src/types/publications.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4549ba60a15ffaa4a73048c8731ea5142edb888e
--- /dev/null
+++ b/src/types/publications.ts
@@ -0,0 +1,9 @@
+export interface StandarizedPublication {
+  pubmedId: string;
+  year: string;
+  journal: string;
+  authors: string;
+  title: string;
+  modelNames: string;
+  elementsIds: string;
+}
diff --git a/src/utils/array/getUniqueArray.ts b/src/utils/array/getUniqueArray.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f4b1603d378b4704994cc8eceea695c9f27bbc6c
--- /dev/null
+++ b/src/utils/array/getUniqueArray.ts
@@ -0,0 +1 @@
+export const getUniqueArray = <T>(elements: T[]): T[] => [...new Set([...elements])];
diff --git a/src/utils/promise/runInSequence.ts b/src/utils/promise/runInSequence.ts
new file mode 100644
index 0000000000000000000000000000000000000000..15d0cca4aa5b3cdbfe709496cad9cc0ebbf2541b
--- /dev/null
+++ b/src/utils/promise/runInSequence.ts
@@ -0,0 +1,32 @@
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable no-await-in-loop */
+import { ONE, ZERO } from '@/constants/common';
+
+const getFuncsInChuncks = <T>(
+  funcs: (() => Promise<T>)[],
+  chunkSize: number,
+): (() => Promise<T>)[][] => {
+  const localFunc = [...funcs];
+  const chunks: (() => Promise<T>)[][] = [];
+
+  while (localFunc.length) {
+    chunks.push(localFunc.splice(ZERO, chunkSize));
+  }
+
+  return chunks;
+};
+
+export const runInSequence = async <T>(
+  funcs: (() => Promise<T>)[],
+  chunkSize: number = ONE,
+): Promise<T[]> => {
+  const chunks = getFuncsInChuncks(funcs, chunkSize);
+  const results: T[] = [];
+
+  for (const chunk of chunks) {
+    const chunkResult = await Promise.all(chunk.map(func => func()));
+    results.push(...chunkResult);
+  }
+
+  return results;
+};