diff --git a/package-lock.json b/package-lock.json
index 225d517d79a374d63e5b3f97ba7717d503763fde..f03034da82f849c388f0ea38401273ee54e4d1ca 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
       "dependencies": {
         "@next/font": "^13.5.2",
         "@reduxjs/toolkit": "^1.9.6",
+        "@tanstack/react-table": "^8.11.7",
         "@types/node": "20.6.2",
         "@types/openlayers": "^4.6.20",
         "@types/react": "18.2.21",
@@ -2038,6 +2039,37 @@
         "tslib": "^2.4.0"
       }
     },
+    "node_modules/@tanstack/react-table": {
+      "version": "8.11.7",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.11.7.tgz",
+      "integrity": "sha512-ZbzfMkLjxUTzNPBXJYH38pv2VpC9WUA+Qe5USSHEBz0dysDTv4z/ARI3csOed/5gmlmrPzVUN3UXGuUMbod3Jg==",
+      "dependencies": {
+        "@tanstack/table-core": "8.11.7"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      },
+      "peerDependencies": {
+        "react": ">=16",
+        "react-dom": ">=16"
+      }
+    },
+    "node_modules/@tanstack/table-core": {
+      "version": "8.11.7",
+      "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.11.7.tgz",
+      "integrity": "sha512-N3ksnkbPbsF3PjubuZCB/etTqvctpXWRHIXTmYfJFnhynQKjeZu8BCuHvdlLPpumKbA+bjY4Ay9AELYLOXPWBg==",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      }
+    },
     "node_modules/@testing-library/dom": {
       "version": "9.3.3",
       "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz",
@@ -15447,6 +15479,19 @@
         "tslib": "^2.4.0"
       }
     },
+    "@tanstack/react-table": {
+      "version": "8.11.7",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.11.7.tgz",
+      "integrity": "sha512-ZbzfMkLjxUTzNPBXJYH38pv2VpC9WUA+Qe5USSHEBz0dysDTv4z/ARI3csOed/5gmlmrPzVUN3UXGuUMbod3Jg==",
+      "requires": {
+        "@tanstack/table-core": "8.11.7"
+      }
+    },
+    "@tanstack/table-core": {
+      "version": "8.11.7",
+      "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.11.7.tgz",
+      "integrity": "sha512-N3ksnkbPbsF3PjubuZCB/etTqvctpXWRHIXTmYfJFnhynQKjeZu8BCuHvdlLPpumKbA+bjY4Ay9AELYLOXPWBg=="
+    },
     "@testing-library/dom": {
       "version": "9.3.3",
       "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz",
diff --git a/package.json b/package.json
index 0f1373dd7e5774c687cb662401a31c64e6d89de1..730be9f5d6988dabee2e69c54847d7ce860defb5 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
   "dependencies": {
     "@next/font": "^13.5.2",
     "@reduxjs/toolkit": "^1.9.6",
+    "@tanstack/react-table": "^8.11.7",
     "@types/node": "20.6.2",
     "@types/openlayers": "^4.6.20",
     "@types/react": "18.2.21",
diff --git a/src/components/FunctionalArea/Modal/Modal.component.test.tsx b/src/components/FunctionalArea/Modal/Modal.component.test.tsx
deleted file mode 100644
index 62644f6644ee65f34abf71dcf5208dd7f195e542..0000000000000000000000000000000000000000
--- a/src/components/FunctionalArea/Modal/Modal.component.test.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { MODAL_INITIAL_STATE } from '@/redux/modal/modal.constants';
-import { modalSelector } from '@/redux/modal/modal.selector';
-import { StoreType } from '@/redux/store';
-import {
-  InitialStoreState,
-  getReduxWrapperWithStore,
-} from '@/utils/testing/getReduxWrapperWithStore';
-import { render, screen } from '@testing-library/react';
-import { Modal } from './Modal.component';
-
-const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => {
-  const { Wrapper, store } = getReduxWrapperWithStore(initialStore);
-  return (
-    render(
-      <Wrapper>
-        <Modal />
-      </Wrapper>,
-    ),
-    {
-      store,
-    }
-  );
-};
-
-describe('Modal - Component', () => {
-  describe('when modal is hidden', () => {
-    beforeEach(() => {
-      renderComponent({
-        modal: {
-          ...MODAL_INITIAL_STATE,
-          isOpen: false,
-          modalTitle: 'Modal Hidden Title',
-        },
-      });
-    });
-
-    it('should modal have hidden class', () => {
-      const modalElement = screen.getByRole('modal');
-
-      expect(modalElement).toBeInTheDocument();
-      expect(modalElement).toHaveClass('hidden');
-    });
-  });
-
-  describe('when modal is shown', () => {
-    let store: StoreType;
-
-    beforeEach(() => {
-      const { store: newStore } = renderComponent({
-        modal: {
-          ...MODAL_INITIAL_STATE,
-          isOpen: true,
-          modalTitle: 'Modal Opened Title',
-        },
-      });
-
-      store = newStore;
-    });
-
-    it('should modal NOT have hidden class', () => {
-      const modalElement = screen.getByRole('modal');
-
-      expect(modalElement).toBeInTheDocument();
-      expect(modalElement).not.toHaveClass('hidden');
-    });
-
-    it('shows modal title', () => {
-      expect(screen.getByText('Modal Opened Title', { exact: false })).toBeInTheDocument();
-    });
-
-    it('shows modal close button', () => {
-      expect(screen.getByLabelText('close button')).toBeInTheDocument();
-    });
-
-    it('closes modal on close button click', () => {
-      const closeButton = screen.getByLabelText('close button');
-
-      closeButton.click();
-
-      const { isOpen } = modalSelector(store.getState());
-
-      expect(isOpen).toBeFalsy();
-    });
-  });
-});
diff --git a/src/components/FunctionalArea/Modal/Modal.component.tsx b/src/components/FunctionalArea/Modal/Modal.component.tsx
index a96fe3f58dd1ceed2671406345a871ed4c638671..e6500c336107407611d0c6b1646b67dccce12202 100644
--- a/src/components/FunctionalArea/Modal/Modal.component.tsx
+++ b/src/components/FunctionalArea/Modal/Modal.component.tsx
@@ -1,13 +1,10 @@
-import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { modalSelector } from '@/redux/modal/modal.selector';
-import { closeModal } from '@/redux/modal/modal.slice';
-import { Icon } from '@/shared/Icon';
 import dynamic from 'next/dynamic';
-import { twMerge } from 'tailwind-merge';
 import { LoginModal } from './LoginModal';
-import { MODAL_ROLE } from './Modal.constants';
 import { OverviewImagesModal } from './OverviewImagesModal';
+import { PublicationsModal } from './PublicationsModal';
+import { ModalLayout } from './ModalLayout';
 import { EditOverlayModal } from './EditOverlayModal';
 
 const MolArtModal = dynamic(
@@ -16,41 +13,31 @@ const MolArtModal = dynamic(
 );
 
 export const Modal = (): React.ReactNode => {
-  const dispatch = useAppDispatch();
-  const { isOpen, modalName, modalTitle } = useAppSelector(modalSelector);
-
-  const handleCloseModal = (): void => {
-    dispatch(closeModal());
-  };
+  const { isOpen, modalName } = useAppSelector(modalSelector);
 
   return (
-    <div
-      className={twMerge(
-        'absolute left-0 top-0 z-10 h-full w-full bg-cetacean-blue/[.48]',
-        isOpen ? '' : 'hidden',
+    <>
+      {isOpen && modalName === 'overview-images' && (
+        <ModalLayout>
+          <OverviewImagesModal />
+        </ModalLayout>
+      )}
+      {isOpen && modalName === 'mol-art' && (
+        <ModalLayout>
+          <MolArtModal />
+        </ModalLayout>
+      )}
+      {isOpen && modalName === 'login' && (
+        <ModalLayout>
+          <LoginModal />
+        </ModalLayout>
+      )}
+      {isOpen && modalName === 'publications' && <PublicationsModal />}
+      {isOpen && modalName === 'edit-overlay' && (
+        <ModalLayout>
+          <EditOverlayModal />
+        </ModalLayout>
       )}
-      role={MODAL_ROLE}
-    >
-      <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',
-            modalName === 'login' && 'h-auto w-[400px]',
-            modalName === 'edit-overlay' && 'h-auto w-[450px]',
-          )}
-        >
-          <div className="flex items-center justify-between bg-white p-[24px] text-xl">
-            <div>{modalTitle}</div>
-            <button type="button" onClick={handleCloseModal} aria-label="close button">
-              <Icon name="close" className="fill-font-500" />
-            </button>
-          </div>
-          {isOpen && modalName === 'overview-images' && <OverviewImagesModal />}
-          {isOpen && modalName === 'mol-art' && <MolArtModal />}
-          {isOpen && modalName === 'login' && <LoginModal />}
-          {isOpen && modalName === 'edit-overlay' && <EditOverlayModal />}
-        </div>
-      </div>
-    </div>
+    </>
   );
 };
diff --git a/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx b/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..792bce0ef02703c7307405858176e8f13a206770
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx
@@ -0,0 +1,44 @@
+import { twMerge } from 'tailwind-merge';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { modalSelector } from '@/redux/modal/modal.selector';
+import { closeModal } from '@/redux/modal/modal.slice';
+import { Icon } from '@/shared/Icon';
+import { MODAL_ROLE } from './ModalLayout.constants';
+
+type ModalLayoutProps = {
+  children: React.ReactNode;
+};
+
+export const ModalLayout = ({ children }: ModalLayoutProps): JSX.Element => {
+  const dispatch = useAppDispatch();
+  const { modalName, modalTitle } = useAppSelector(modalSelector);
+
+  const handleCloseModal = (): void => {
+    dispatch(closeModal());
+  };
+
+  return (
+    <div
+      className={twMerge('absolute left-0 top-0 z-10 h-full w-full bg-cetacean-blue/[.48]')}
+      role={MODAL_ROLE}
+    >
+      <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',
+            modalName === 'login' && 'h-auto w-[400px]',
+          )}
+        >
+          <div className="flex items-center justify-between bg-white p-[24px] text-xl">
+            <div>{modalTitle}</div>
+            <button type="button" onClick={handleCloseModal} aria-label="close button">
+              <Icon name="close" className="fill-font-500" />
+            </button>
+          </div>
+          {children}
+        </div>
+      </div>
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/Modal.constants.ts b/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.constants.ts
similarity index 100%
rename from src/components/FunctionalArea/Modal/Modal.constants.ts
rename to src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.constants.ts
diff --git a/src/components/FunctionalArea/Modal/ModalLayout/index.ts b/src/components/FunctionalArea/Modal/ModalLayout/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..357e7d05cc785bba2a1e0a2945d6e6feb4e82467
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/ModalLayout/index.ts
@@ -0,0 +1 @@
+export { ModalLayout } from './ModalLayout.component';
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModal.test.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModal.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4df51d1e1fd12dbfe315b8e639981c44d7ea554a
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModal.test.tsx
@@ -0,0 +1,29 @@
+describe('Publications Modal - component', () => {
+  it('should render number of publications', () => {});
+  it('should render download csv button', () => {});
+  it('should trigger download on csv button click', () => {});
+  it('should render search input', () => {});
+  it('should be able to search publications by using search input', () => {});
+  it('should be able to sort publications by clicking on Pubmed ID column header', () => {});
+  it('should be able to sort publications by clicking on Title column header', () => {});
+  it('should be able to sort publications by clicking on Authors column header', () => {});
+  it('should be able to sort publications by clicking on Journal column header', () => {});
+  it('should be able to sort publications by clicking on Year column header', () => {});
+  it('should be able to sort publications by clicking on Elements on map column header', () => {});
+  it('should be able to sort publications by clicking on SUBMAPS on map column header', () => {});
+
+  it('should render publications list', () => {});
+
+  it('should render pagination', () => {});
+  it('should be able to navigate to next page', () => {});
+  it('should be able to navigate to previous page', () => {});
+
+  describe('submaps filter', () => {
+    it('should render submaps filter', () => {});
+    it('should have no default submap selected on init on submaps filter', () => {});
+    it('should display publications for selected submap', () => {});
+    it('should display publications related to selected submap when submap is selected', () => {});
+    it('should display publications related to selected submap when user searches for publications using search input', () => {});
+    it('should display publications related to selected submap when user sorts publications by clicking on column header', () => {});
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModal.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0414f4a91080fe9eb5d2f091b2b940ab0662e67b
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModal.tsx
@@ -0,0 +1,46 @@
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { getPublications } from '@/redux/publications/publications.thunks';
+import { useEffect, useMemo } from 'react';
+import { publicationsListDataSelector } from '@/redux/publications/publications.selectors';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { modelsNameMapSelector } from '@/redux/models/models.selectors';
+import { FIRST_ARRAY_ELEMENT } from '@/constants/common';
+import { LoadingIndicator } from '@/shared/LoadingIndicator';
+import {
+  PublicationsTable,
+  PublicationsTableData,
+} from './PublicationsTable/PublicationsTable.component';
+import { PublicationsModalLayout } from './PublicationsModalLayout';
+
+export const PublicationsModal = (): JSX.Element => {
+  const dispatch = useAppDispatch();
+  const data = useAppSelector(publicationsListDataSelector);
+  const mapsNames = useAppSelector(modelsNameMapSelector);
+
+  const parsedData: PublicationsTableData[] | undefined = useMemo(() => {
+    const dd = data?.map(item => ({
+      pubmedId: item.publication.article.pubmedId,
+      title: item.publication.article.title,
+      authors: item.publication.article.authors,
+      journal: item.publication.article.journal,
+      year: item.publication.article.year,
+      elementsOnMap: '{link to element on map}',
+      submaps: mapsNames[item.elements[FIRST_ARRAY_ELEMENT].modelId],
+    }));
+    return dd || [];
+  }, [data, mapsNames]);
+
+  useEffect(() => {
+    if (!data) {
+      dispatch(getPublications({ params: {} }));
+    }
+  }, [data, dispatch]);
+
+  return (
+    <PublicationsModalLayout>
+      <div className="flex w-full flex-1 flex-col items-center justify-center overflow-hidden bg-white">
+        {data ? <PublicationsTable data={parsedData} /> : <LoadingIndicator />}
+      </div>
+    </PublicationsModalLayout>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8d92fe190e0cc714de83150de5718c367d08001f
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.component.tsx
@@ -0,0 +1,45 @@
+import { twMerge } from 'tailwind-merge';
+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 { PublicationsSearch } from '../PublicationsSearch';
+
+type ModalLayoutProps = {
+  children: React.ReactNode;
+};
+
+export const PublicationsModalLayout = ({ children }: ModalLayoutProps): JSX.Element => {
+  const dispatch = useAppDispatch();
+  const numberOfPublications = useAppSelector(filteredSizeSelector);
+
+  const handleCloseModal = (): void => {
+    dispatch(closeModal());
+  };
+
+  return (
+    <div
+      className={twMerge('absolute left-0 top-0 z-10 h-full w-full bg-cetacean-blue/[.48]')}
+      role={MODAL_ROLE}
+    >
+      <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>
+            <div className="flex flex-row flex-nowrap items-center">
+              <PublicationsSearch />
+              <button type="button" onClick={handleCloseModal} aria-label="close button">
+                <Icon name="close" className="fill-font-500" />
+              </button>
+            </div>
+          </div>
+          {children}
+        </div>
+      </div>
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.constants.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b31cdcfb5d1c572a9a9bc64409d465267c937779
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/PublicationsModalLayout.constants.ts
@@ -0,0 +1 @@
+export const MODAL_ROLE = 'modal';
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/index.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..88be5bc7f605798c06ce9036cd5e9f5cc16b90c9
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/index.ts
@@ -0,0 +1 @@
+export { PublicationsModalLayout } from './PublicationsModalLayout.component';
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsSearch/PublicationsSearch.component.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsSearch/PublicationsSearch.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..dd90e7d2739b3749bcef54b53bf28775fc44b73c
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsSearch/PublicationsSearch.component.tsx
@@ -0,0 +1,74 @@
+import { ChangeEvent, useCallback, useEffect, useState } from 'react';
+import lensIcon from '@/assets/vectors/icons/lens.svg';
+import { useDebounce } from 'use-debounce';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { getPublications } from '@/redux/publications/publications.thunks';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import {
+  isLoadingSelector,
+  selectedModelIdSelector,
+  sortColumnSelector,
+  sortOrderSelector,
+} from '@/redux/publications/publications.selectors';
+import Image from 'next/image';
+import { setPublicationSearchValue } from '@/redux/publications/publications.slice';
+import { DEFAULT_PAGE_SIZE } from '../PublicationsTable/PublicationsTable.constants';
+
+const DEFAULT_DELAY = 500;
+
+export const PublicationsSearch = (): JSX.Element => {
+  const dispatch = useAppDispatch();
+  const isLoading = useAppSelector(isLoadingSelector);
+  const [value, setValue] = useState('');
+  const [debouncedValue] = useDebounce<string>(value, DEFAULT_DELAY);
+  const sortColumn = useAppSelector(sortColumnSelector);
+  const sortOrder = useAppSelector(sortOrderSelector);
+  const selectedId = useAppSelector(selectedModelIdSelector);
+
+  const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
+    setValue(event.target.value);
+  };
+
+  const handleSearch = useCallback((): void => {
+    dispatch(
+      getPublications({
+        params: {
+          page: 0,
+          length: DEFAULT_PAGE_SIZE,
+          sortColumn,
+          sortOrder,
+          search: debouncedValue,
+        },
+        modelId: selectedId,
+      }),
+    );
+  }, [debouncedValue, dispatch, selectedId, sortColumn, sortOrder]);
+
+  useEffect(() => {
+    dispatch(setPublicationSearchValue(debouncedValue));
+    handleSearch();
+  }, [debouncedValue, dispatch, handleSearch]);
+
+  return (
+    <div className="relative mr-4">
+      <input
+        value={value}
+        name="search-input"
+        aria-label="search-input"
+        data-testid="search-input"
+        onChange={handleChange}
+        disabled={isLoading}
+        className="h-9 w-72 rounded-[64px] border border-transparent bg-cultured px-4 py-2.5 text-xs font-medium text-font-400 outline-none  hover:border-greyscale-600 focus:border-greyscale-600"
+      />
+      <button disabled={isLoading} type="button" className="bg-transparent">
+        <Image
+          src={lensIcon}
+          alt="lens icon"
+          height={16}
+          width={16}
+          className="absolute right-4 top-2.5"
+        />
+      </button>
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsSearch/index.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsSearch/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b7e9612330ef61a9b109084f1198c4e038cf4af6
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsSearch/index.ts
@@ -0,0 +1 @@
+export { PublicationsSearch } from './PublicationsSearch.component';
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/FilterBySubmapHeader/FilterBySubmapHeader.component.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/FilterBySubmapHeader/FilterBySubmapHeader.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ab7b969de1ca4c68c969fc2ac88c6c2caf5d317f
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/FilterBySubmapHeader/FilterBySubmapHeader.component.tsx
@@ -0,0 +1,96 @@
+import { useSelect } from 'downshift';
+import { twMerge } from 'tailwind-merge';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { modelsIdsAndNamesSelector } from '@/redux/models/models.selectors';
+import {
+  searchValueSelector,
+  sortColumnSelector,
+  sortOrderSelector,
+} from '@/redux/publications/publications.selectors';
+import { getPublications } from '@/redux/publications/publications.thunks';
+import { setSelectedModelId } from '@/redux/publications/publications.slice';
+import { Icon } from '@/shared/Icon';
+import { DEFAULT_PAGE_SIZE } from '../PublicationsTable.constants';
+
+export const FilterBySubmapHeader = (): JSX.Element => {
+  const dispatch = useAppDispatch();
+  const models = useAppSelector(modelsIdsAndNamesSelector);
+  const sortColumn = useAppSelector(sortColumnSelector);
+  const sortOrder = useAppSelector(sortOrderSelector);
+  const searchValue = useAppSelector(searchValueSelector);
+
+  const handleChange = (modelId: number | undefined): void => {
+    const newModelId = modelId ? String(modelId) : undefined;
+
+    dispatch(setSelectedModelId(newModelId));
+
+    dispatch(
+      getPublications({
+        params: {
+          page: 0,
+          length: DEFAULT_PAGE_SIZE,
+          sortColumn,
+          sortOrder,
+          search: searchValue,
+        },
+        modelId: newModelId,
+      }),
+    );
+  };
+
+  const {
+    isOpen,
+    selectedItem,
+    getToggleButtonProps,
+    getMenuProps,
+    highlightedIndex,
+    getItemProps,
+  } = useSelect({
+    items: models,
+    initialSelectedItem: null,
+    onSelectedItemChange: ({ selectedItem: newSelectedItem }) => handleChange(newSelectedItem?.id),
+  });
+
+  return (
+    <div className="relative">
+      <div
+        className="flex cursor-pointer flex-row items-center justify-between bg-white px-3"
+        {...getToggleButtonProps()}
+        data-testid="background-dropdown-button"
+      >
+        <span data-testid="background-dropdown-button-name" className="truncate font-semibold">
+          {selectedItem?.name || 'Submaps'}
+        </span>
+        <Icon
+          name="chevron-down"
+          className={twMerge('arrow-button h-6 w-6 fill-primary-500', isOpen && 'rotate-180')}
+        />
+      </div>
+      <ul
+        {...getMenuProps()}
+        className={twMerge(
+          'absolute top-full   mt-2 h-60 w-full overflow-y-scroll  bg-white shadow-lg',
+          !isOpen && 'hidden',
+        )}
+        data-testid="background-dropdown-list"
+      >
+        {isOpen &&
+          models.map((item, index) => (
+            <li
+              key={item.id}
+              {...getItemProps({ item, index })}
+              className={twMerge(
+                'w-full truncate border-t text-left font-normal',
+                highlightedIndex === index && 'text-primary-500',
+                selectedItem?.id === item.id && 'font-bold',
+                'flex flex-col px-4 py-2 shadow-sm',
+              )}
+            >
+              {item.name}
+            </li>
+          ))}
+      </ul>
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/FilterBySubmapHeader/FilterBySubmapHeader.test.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/FilterBySubmapHeader/FilterBySubmapHeader.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6dfca3c78f23ed04dfc05793df00d544d0842569
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/FilterBySubmapHeader/FilterBySubmapHeader.test.tsx
@@ -0,0 +1,164 @@
+/* eslint-disable no-magic-numbers */
+import { Provider } from 'react-redux';
+import configureMockStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import { act } from 'react-dom/test-utils';
+import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import {
+  InitialStoreState,
+  getReduxWrapperWithStore,
+} from '@/utils/testing/getReduxWrapperWithStore';
+import { StoreType } from '@/redux/store';
+import { render, screen } from '@testing-library/react';
+import { PUBLICATIONS_INITIAL_STATE_MOCK } from '@/redux/publications/publications.mock';
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { MODELS_MOCK } from '@/models/mocks/modelsMock';
+import { FIRST_ARRAY_ELEMENT, ZERO } from '@/constants/common';
+import { FilterBySubmapHeader } from './FilterBySubmapHeader.component';
+
+mockNetworkResponse();
+
+const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
+
+  return (
+    render(
+      <Wrapper>
+        <FilterBySubmapHeader />
+      </Wrapper>,
+    ),
+    {
+      store,
+    }
+  );
+};
+
+describe('FilterBySubmapHeader - component', () => {
+  describe('render', () => {
+    it('should render closed dropdown', async () => {
+      renderComponent({
+        publications: PUBLICATIONS_INITIAL_STATE_MOCK,
+        models: { data: MODELS_MOCK, loading: 'idle', error: DEFAULT_ERROR },
+      });
+
+      const ulTag = await screen.findByTestId('background-dropdown-list');
+      const listItems = screen.queryAllByRole('listitem');
+
+      expect(listItems).toHaveLength(ZERO);
+      expect(ulTag).toBeInTheDocument();
+      expect(ulTag).toHaveClass('hidden');
+    });
+
+    it('should render available submaps on dropdown open', async () => {
+      renderComponent({
+        publications: PUBLICATIONS_INITIAL_STATE_MOCK,
+        models: { data: MODELS_MOCK, loading: 'idle', error: DEFAULT_ERROR },
+      });
+
+      const button = await screen.findByTestId('background-dropdown-button');
+      await act(() => {
+        button.click();
+      });
+
+      const ulTag = await screen.findByTestId('background-dropdown-list');
+      expect(ulTag).not.toHaveClass('hidden');
+
+      const listItems = await screen.findAllByRole('option');
+      expect(listItems).toHaveLength(MODELS_MOCK.length);
+      expect(listItems[FIRST_ARRAY_ELEMENT]).toHaveTextContent(
+        MODELS_MOCK[FIRST_ARRAY_ELEMENT].name,
+      );
+    });
+
+    it('should display no value selected initially', () => {
+      renderComponent({
+        publications: PUBLICATIONS_INITIAL_STATE_MOCK,
+        models: { data: MODELS_MOCK, loading: 'idle', error: DEFAULT_ERROR },
+      });
+
+      const button = screen.getByTestId('background-dropdown-button-name');
+      expect(button).toHaveTextContent('Submaps');
+    });
+
+    it('should display selected submap name in toggle button', async () => {
+      renderComponent({
+        publications: PUBLICATIONS_INITIAL_STATE_MOCK,
+        models: { data: MODELS_MOCK, loading: 'idle', error: DEFAULT_ERROR },
+      });
+
+      const button = await screen.findByTestId('background-dropdown-button');
+      await act(() => {
+        button.click();
+      });
+
+      const listItems = screen.getAllByRole('option');
+      const selectedItem = listItems[FIRST_ARRAY_ELEMENT];
+      await act(() => {
+        selectedItem.click();
+      });
+
+      const buttonName = screen.getByTestId('background-dropdown-button-name');
+      expect(buttonName).toHaveTextContent(MODELS_MOCK[FIRST_ARRAY_ELEMENT].name);
+    });
+  });
+
+  describe('on submap selection', () => {
+    it('should dispatch setSelectedModelId action', async () => {
+      const mockStore = configureMockStore([thunk]);
+      const store = mockStore({
+        publications: PUBLICATIONS_INITIAL_STATE_MOCK,
+        models: { data: MODELS_MOCK, loading: 'idle', error: DEFAULT_ERROR },
+      });
+      render(
+        <Provider store={store}>
+          <FilterBySubmapHeader />
+        </Provider>,
+      );
+
+      const button = await screen.findByTestId('background-dropdown-button');
+      await act(() => {
+        button.click();
+      });
+
+      const listItems = screen.getAllByRole('option');
+      const selectedItem = listItems[FIRST_ARRAY_ELEMENT];
+      await act(() => {
+        selectedItem.click();
+      });
+
+      const actions = store.getActions();
+      expect(actions).toHaveLength(3); // 2 - getPublications (pending, fulfilled), 1 - setSelectedModelId
+      expect(actions[FIRST_ARRAY_ELEMENT].type).toBe('publications/setSelectedModelId');
+
+      const selectedModelId = actions[FIRST_ARRAY_ELEMENT].payload;
+      expect(selectedModelId).toBe(String(MODELS_MOCK[FIRST_ARRAY_ELEMENT].idObject));
+    });
+    it('should dispatch getPublications action', async () => {
+      const mockStore = configureMockStore([thunk]);
+      const store = mockStore({
+        publications: PUBLICATIONS_INITIAL_STATE_MOCK,
+        models: { data: MODELS_MOCK, loading: 'idle', error: DEFAULT_ERROR },
+      });
+      render(
+        <Provider store={store}>
+          <FilterBySubmapHeader />
+        </Provider>,
+      );
+
+      const button = await screen.findByTestId('background-dropdown-button');
+      await act(() => {
+        button.click();
+      });
+
+      const listItems = screen.getAllByRole('option');
+      const selectedItem = listItems[FIRST_ARRAY_ELEMENT];
+      await act(() => {
+        selectedItem.click();
+      });
+
+      const actions = store.getActions();
+      expect(actions).toHaveLength(3); // 2 - getPublications (pending, fulfilled), 1 - setSelectedModelId
+      expect(actions[1].type).toBe('publications/getPublications/pending');
+    });
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/FilterBySubmapHeader/index.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/FilterBySubmapHeader/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.component.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..08cf5805195585f6902bdd2ba02bada9c3dabf42
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.component.tsx
@@ -0,0 +1,206 @@
+import { ONE, ZERO } from '@/constants/common';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import {
+  totalSizeSelector,
+  paginationSelector,
+  isLoadingSelector,
+  sortColumnSelector,
+  sortOrderSelector,
+  selectedModelIdSelector,
+  searchValueSelector,
+} from '@/redux/publications/publications.selectors';
+import { getPublications } from '@/redux/publications/publications.thunks';
+import { Button } from '@/shared/Button';
+import {
+  PaginationState,
+  createColumnHelper,
+  flexRender,
+  getCoreRowModel,
+  useReactTable,
+  OnChangeFn,
+} from '@tanstack/react-table';
+import { useState } from 'react';
+import { SortByHeader } from './SortByHeader';
+import { DEFAULT_PAGE_SIZE } from './PublicationsTable.constants';
+import { FilterBySubmapHeader } from './FilterBySubmapHeader/FilterBySubmapHeader.component';
+
+export type PublicationsTableData = {
+  pubmedId: string;
+  title: string;
+  authors: string[];
+  journal: string;
+  year: number;
+  elementsOnMap: string;
+  submaps: string;
+};
+
+const columnHelper = createColumnHelper<PublicationsTableData>();
+
+const columns = [
+  columnHelper.accessor(row => row.pubmedId, {
+    id: 'pubmedId',
+    header: () => <SortByHeader columnName="pubmedId">Pubmed ID</SortByHeader>,
+    size: 128,
+  }),
+  columnHelper.accessor(row => row.title, {
+    id: 'title',
+    header: () => <SortByHeader columnName="title">Title</SortByHeader>,
+    size: 288,
+  }),
+  columnHelper.accessor(row => row.authors, {
+    id: 'authors',
+    header: () => <SortByHeader columnName="authors">Authors</SortByHeader>,
+    size: 200,
+  }),
+  columnHelper.accessor(row => row.journal, {
+    id: 'journal',
+    header: () => <SortByHeader columnName="journal">Journal</SortByHeader>,
+    size: 168,
+  }),
+  columnHelper.accessor(row => row.year, {
+    id: 'year',
+    header: () => <SortByHeader columnName="year">Year</SortByHeader>,
+    size: 80,
+  }),
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  columnHelper.accessor(row => row.elementsOnMap, { header: 'Elements on map', size: 176 }),
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  columnHelper.accessor(row => row.submaps, {
+    id: 'submaps',
+    header: () => <FilterBySubmapHeader />,
+    size: 144,
+  }),
+];
+
+type PublicationsTableProps = {
+  data: PublicationsTableData[];
+};
+
+export const PublicationsTable = ({ data }: PublicationsTableProps): JSX.Element => {
+  const dispatch = useAppDispatch();
+  const pagesCount = useAppSelector(totalSizeSelector);
+  const isPublicationsLoading = useAppSelector(isLoadingSelector);
+  const sortColumn = useAppSelector(sortColumnSelector);
+  const sortOrder = useAppSelector(sortOrderSelector);
+  const selectedId = useAppSelector(selectedModelIdSelector);
+  const searchValue = useAppSelector(searchValueSelector);
+
+  const reduxPagination = useAppSelector(paginationSelector);
+  const [pagination, setPagination] = useState(reduxPagination);
+
+  const onPaginationChange: OnChangeFn<PaginationState> = updater => {
+    /** updating state this way is forced by table library */
+    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+    // @ts-ignore
+    const nextState = updater(pagination);
+
+    dispatch(
+      getPublications({
+        params: {
+          page: nextState.pageIndex,
+          length: DEFAULT_PAGE_SIZE,
+          sortColumn,
+          sortOrder,
+          search: searchValue,
+        },
+        modelId: selectedId,
+      }),
+    );
+    setPagination(nextState);
+  };
+
+  const table = useReactTable({
+    state: {
+      pagination,
+    },
+    columns,
+    data,
+    getCoreRowModel: getCoreRowModel(),
+    manualPagination: true,
+    pageCount: pagesCount,
+    // onPaginationChange: setPagination,
+    onPaginationChange,
+  });
+
+  return (
+    <div className="flex max-h-full w-full flex-col items-center justify-center bg-white p-6">
+      <div className="w-full overflow-auto">
+        <table className="w-full min-w-[1184px] table-auto overflow-auto text-sm">
+          <thead className="sticky top-0 bg-white-pearl">
+            {table.getHeaderGroups().map(headerGroup => (
+              <tr key={headerGroup.id} className="border-y ">
+                {headerGroup.headers.map(header => (
+                  <th
+                    key={header.id}
+                    className="whitespace-nowrap py-2.5"
+                    style={{ width: header.getSize() }}
+                  >
+                    {flexRender(header.column.columnDef.header, header.getContext())}
+                  </th>
+                ))}
+              </tr>
+            ))}
+          </thead>
+          <tbody>
+            {data &&
+              table.getRowModel().rows.map(row => (
+                <tr key={row.id} className="even:bg-lotion">
+                  {row.getVisibleCells().map(cell => (
+                    <td
+                      key={cell.id}
+                      className="p-3"
+                      style={{
+                        width: cell.column.getSize(),
+                      }}
+                    >
+                      {flexRender(cell.column.columnDef.cell, cell.getContext())}
+                    </td>
+                  ))}
+                </tr>
+              ))}
+          </tbody>
+        </table>
+      </div>
+      <div className="flex w-full flex-row justify-end border-t">
+        <div className="mt-6 flex flex-row items-center">
+          <Button
+            variantStyles="quiet"
+            className="text-primary-500"
+            onClick={() => table.setPageIndex(ZERO)}
+            disabled={isPublicationsLoading}
+          >
+            First page
+          </Button>
+          <Button
+            variantStyles="secondary"
+            onClick={() => table.previousPage()}
+            disabled={isPublicationsLoading}
+          >
+            Previous page
+          </Button>
+
+          <div className="mx-4 text-sm font-semibold">
+            Page {table.getState().pagination.pageIndex + ONE} out of {table.getPageCount()}
+          </div>
+
+          <Button
+            variantStyles="secondary"
+            onClick={() => table.nextPage()}
+            disabled={isPublicationsLoading}
+          >
+            Next page
+          </Button>
+          <Button
+            variantStyles="quiet"
+            className="text-primary-500"
+            onClick={() => table.setPageIndex(table.getPageCount() - ONE)}
+            disabled={isPublicationsLoading}
+          >
+            Last page
+          </Button>
+        </div>
+      </div>
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.constants.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..459723e867c1e713d2867cdf21e7784751a627dc
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.constants.ts
@@ -0,0 +1 @@
+export const DEFAULT_PAGE_SIZE = 10;
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/SortByHeader/SortByHeader.component.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/SortByHeader/SortByHeader.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..27c9aeacece89b5386e7ad91f2946dc375c46b06
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/SortByHeader/SortByHeader.component.tsx
@@ -0,0 +1,72 @@
+import { useEffect, useState } from 'react';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { setSortOrderAndColumn } from '@/redux/publications/publications.slice';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { Icon } from '@/shared/Icon';
+import {
+  searchValueSelector,
+  sortColumnSelector,
+} from '@/redux/publications/publications.selectors';
+import { SortColumn, SortOrder } from '@/redux/publications/publications.types';
+import { getPublications } from '@/redux/publications/publications.thunks';
+import { DEFAULT_PAGE_SIZE } from '../PublicationsTable.constants';
+
+type SortByHeaderProps = {
+  columnName: SortColumn;
+  children: React.ReactNode;
+};
+
+export const SortByHeader = ({ columnName, children }: SortByHeaderProps): JSX.Element => {
+  const activeColumn = useAppSelector(sortColumnSelector);
+  const [sortDirection, setSortDirection] = useState<SortOrder | undefined>();
+  const searchValue = useAppSelector(searchValueSelector);
+  const dispatch = useAppDispatch();
+  // if columnName is the same as the current sortColumn, then sort in the opposite direction
+
+  const handleSortBy = (): void => {
+    const newSortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
+    setSortDirection(newSortDirection);
+    dispatch(
+      setSortOrderAndColumn({
+        sortColumn: columnName,
+        sortOrder: newSortDirection,
+      }),
+    );
+
+    dispatch(
+      getPublications({
+        params: {
+          page: 0,
+          length: DEFAULT_PAGE_SIZE,
+          sortColumn: columnName,
+          sortOrder: newSortDirection,
+          search: searchValue,
+        },
+      }),
+    );
+  };
+
+  useEffect(() => {
+    if (activeColumn === columnName) {
+      setSortDirection('asc');
+    } else {
+      setSortDirection(undefined);
+    }
+  }, [activeColumn, columnName]);
+
+  return (
+    <div className="flex flex-row items-center px-3">
+      <button type="button" onClick={handleSortBy}>
+        {children}
+      </button>
+      <div className="relative ml-2 flex h-6 w-4 flex-col">
+        {sortDirection !== 'desc' && (
+          <Icon name="arrow" className="absolute top-0 h-4 w-4 rotate-[270deg] fill-font-500" />
+        )}
+        {sortDirection !== 'asc' && (
+          <Icon name="arrow" className="absolute bottom-0 h-4 w-4 rotate-90 fill-font-500" />
+        )}
+      </div>
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/SortByHeader/index.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/SortByHeader/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc286d4cab14498e77a0c792d0efd88568886571
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/SortByHeader/index.ts
@@ -0,0 +1 @@
+export { SortByHeader } from './SortByHeader.component';
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/index.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/index.ts b/src/components/FunctionalArea/Modal/PublicationsModal/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea5f74a5c47ed3034b8edb452ef072c7271bcff7
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/index.ts
@@ -0,0 +1 @@
+export { PublicationsModal } from './PublicationsModal';
diff --git a/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx b/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx
index 318239811d4e068808bcf99f604719c7d577e9a7..a006e829e5515fb7eb53d8bac0b397187ed4f2e7 100644
--- a/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx
+++ b/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx
@@ -12,8 +12,12 @@ import { apiPath } from '@/redux/apiPath';
 import { LinkButton } from '@/shared/LinkButton';
 import { mainMapModelDescriptionSelector } from '@/redux/models/models.selectors';
 import './ProjectInfoDrawer.styles.css';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { useEffect } from 'react';
+import { openPublicationsModal } from '@/redux/modal/modal.slice';
 
 export const ProjectInfoDrawer = (): JSX.Element => {
+  const dispatch = useAppDispatch();
   const diseaseName = useAppSelector(diseaseNameSelector);
   const diseaseLink = useAppSelector(diseaseLinkSelector);
   const organismLink = useAppSelector(organismLinkSelector);
@@ -24,6 +28,14 @@ export const ProjectInfoDrawer = (): JSX.Element => {
 
   const sourceDownloadLink = window.location.hostname + apiPath.getSourceFile();
 
+  useEffect(() => {
+    // dispatch(getPublications());
+  }, [dispatch]);
+
+  const onPublicationsClick = (): void => {
+    dispatch(openPublicationsModal());
+  };
+
   return (
     <div data-testid="export-drawer" className="h-full max-h-full">
       <DrawerHeading title="Project info" />
@@ -36,13 +48,17 @@ export const ProjectInfoDrawer = (): JSX.Element => {
         </p>
         <div className="mt-4">Data:</div>
         <ul className="list-disc pl-6 ">
-          <li className="mt-2 text-hyperlink-blue">(21) publications</li>
+          <li className="mt-2 text-hyperlink-blue">
+            <button type="button" onClick={onPublicationsClick} className="text-sm font-semibold">
+              (21) publications
+            </button>
+          </li>
           <li className="mt-2 text-hyperlink-blue">
             <a
               href="https://minerva.pages.uni.lu/doc/"
               target="_blank"
               rel="noopener noreferrer"
-              className="hover:underline"
+              className="font-semibold hover:underline"
             >
               Manual
             </a>
@@ -53,7 +69,7 @@ export const ProjectInfoDrawer = (): JSX.Element => {
               href={diseaseLink}
               target="_blank"
               rel="noopener noreferrer"
-              className="hover:underline"
+              className="font-semibold hover:underline"
             >
               {diseaseName}
             </a>
@@ -64,7 +80,7 @@ export const ProjectInfoDrawer = (): JSX.Element => {
               href={organismLink}
               target="_blank"
               rel="noopener noreferrer"
-              className="hover:underline"
+              className="font-semibold hover:underline"
             >
               {organismName}
             </a>
diff --git a/src/models/publicationsResponseSchema.ts b/src/models/publicationsResponseSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5bdcca5b4d1a1bd5540cb23832957567acdfc559
--- /dev/null
+++ b/src/models/publicationsResponseSchema.ts
@@ -0,0 +1,10 @@
+import { z } from 'zod';
+import { publicationSchema } from './publicationsSchema';
+
+export const publicationsResponseSchema = z.object({
+  data: z.array(publicationSchema),
+  totalSize: z.number(),
+  filteredSize: z.number(),
+  length: z.number(),
+  page: z.number(),
+});
diff --git a/src/models/publicationsSchema.ts b/src/models/publicationsSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b1574088a29007686de243593f69566a074b2ede
--- /dev/null
+++ b/src/models/publicationsSchema.ts
@@ -0,0 +1,10 @@
+import { z } from 'zod';
+import { targetElementSchema } from './targetElementSchema';
+import { articleSchema } from './articleSchema';
+
+export const publicationSchema = z.object({
+  elements: z.array(targetElementSchema),
+  publication: z.object({
+    article: articleSchema,
+  }),
+});
diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts
index 5193c5e4865e5ad7d21c71896c3e666f27392ed9..ac815f25ece6507d4ad61f5ebc2a8bc87dc6e1f9 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -1,6 +1,23 @@
 import { PROJECT_ID } from '@/constants';
 import { Point } from '@/types/map';
 import { PerfectSearchParams } from '@/types/search';
+import { GetPublicationsParams, PublicationsQueryParams } from './publications/publications.types';
+
+const getPublicationsURLSearchParams = (
+  providedParams: PublicationsQueryParams,
+): URLSearchParams => {
+  const params = new URLSearchParams();
+
+  const validProvidedParamsArray = Object.entries(providedParams).filter(([, value]) =>
+    Boolean(value),
+  );
+
+  validProvidedParamsArray.forEach(([key, value]) => {
+    params.append(key, value.toString());
+  });
+
+  return params;
+};
 
 export const apiPath = {
   getBioEntityContentsStringWithQuery: ({
@@ -63,6 +80,10 @@ export const apiPath = {
   getSourceFile: (): string => `/projects/${PROJECT_ID}:downloadSource`,
   getMesh: (meshId: string): string => `mesh/${meshId}`,
   getTaxonomy: (taxonomyId: string): string => `taxonomy/${taxonomyId}`,
+  getPublications: ({ params, modelId = '*' }: GetPublicationsParams): string =>
+    `/projects/${PROJECT_ID}/models/${modelId}/publications/?${getPublicationsURLSearchParams(
+      params,
+    )}`,
   registerPluign: (): string => `plugins/`,
   getPlugin: (pluginId: string): string => `plugins/${pluginId}/`,
   getAllPlugins: (): string => `/plugins/`,
diff --git a/src/redux/modal/modal.reducers.ts b/src/redux/modal/modal.reducers.ts
index 9308ed4df46feef22d3fdf449dc9683e81fb12e5..2cda42b432235ac40f371a35be3f93fe6629f653 100644
--- a/src/redux/modal/modal.reducers.ts
+++ b/src/redux/modal/modal.reducers.ts
@@ -51,6 +51,12 @@ export const setOverviewImageIdReducer = (
   };
 };
 
+export const openPublicationsModalReducer = (state: ModalState): void => {
+  state.isOpen = true;
+  state.modalName = 'publications';
+  state.modalTitle = 'Publications';
+};
+
 export const openEditOverlayModalReducer = (
   state: ModalState,
   action: OpenEditOverlayModalAction,
diff --git a/src/redux/modal/modal.slice.ts b/src/redux/modal/modal.slice.ts
index c7b9feb6dbf9d8f7374ba547cd5277459356d7b5..75de9c430304b2829e30352a9ffb1561cb39336e 100644
--- a/src/redux/modal/modal.slice.ts
+++ b/src/redux/modal/modal.slice.ts
@@ -7,6 +7,7 @@ import {
   openOverviewImagesModalByIdReducer,
   openMolArtModalByIdReducer,
   setOverviewImageIdReducer,
+  openPublicationsModalReducer,
   openEditOverlayModalReducer,
 } from './modal.reducers';
 
@@ -20,6 +21,7 @@ const modalSlice = createSlice({
     openMolArtModalById: openMolArtModalByIdReducer,
     setOverviewImageId: setOverviewImageIdReducer,
     openLoginModal: openLoginModalReducer,
+    openPublicationsModal: openPublicationsModalReducer,
     openEditOverlayModal: openEditOverlayModalReducer,
   },
 });
@@ -31,6 +33,7 @@ export const {
   setOverviewImageId,
   openMolArtModalById,
   openLoginModal,
+  openPublicationsModal,
   openEditOverlayModal,
 } = modalSlice.actions;
 
diff --git a/src/redux/models/models.selectors.ts b/src/redux/models/models.selectors.ts
index 652c018f1f3cdf8db33587a5c3972b3f01f87848..99d94b7648a22f1d07d77ac63cdf69592d55c0f9 100644
--- a/src/redux/models/models.selectors.ts
+++ b/src/redux/models/models.selectors.ts
@@ -17,6 +17,17 @@ export const modelsIdsSelector = createSelector(modelsDataSelector, models =>
   models.map(model => model.idObject),
 );
 
+export const modelsNameMapSelector = createSelector(modelsDataSelector, models =>
+  models.reduce(
+    (acc, model) => ({ ...acc, [model.idObject]: model.name }),
+    {} as Record<number, string>,
+  ),
+);
+
+export const modelsIdsAndNamesSelector = createSelector(modelsDataSelector, models =>
+  models.map(({ idObject, name }) => ({ id: idObject, name })),
+);
+
 export const currentModelIdSelector = createSelector(
   currentModelSelector,
   model => model?.idObject || MODEL_ID_DEFAULT,
diff --git a/src/redux/publications/publications.mock.ts b/src/redux/publications/publications.mock.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3ae459cd1a5e4a1bad2f273d6c20bb6336d46cd7
--- /dev/null
+++ b/src/redux/publications/publications.mock.ts
@@ -0,0 +1,11 @@
+import { PublicationsState } from './publications.types';
+
+export const PUBLICATIONS_INITIAL_STATE_MOCK: PublicationsState = {
+  loading: 'idle',
+  data: undefined,
+  error: { name: '', message: '' },
+  sortColumn: '',
+  sortOrder: 'asc',
+  searchValue: '',
+  selectedModelId: undefined,
+};
diff --git a/src/redux/publications/publications.reducers.ts b/src/redux/publications/publications.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3ae1b8d400214b65232c8ccf3275c0bc17b99a10
--- /dev/null
+++ b/src/redux/publications/publications.reducers.ts
@@ -0,0 +1,41 @@
+import { ActionReducerMapBuilder, PayloadAction } from '@reduxjs/toolkit';
+import { PublicationsState, SortColumn, SortOrder } from './publications.types';
+import { getPublications } from './publications.thunks';
+
+export const getPublicationsReducer = (
+  builder: ActionReducerMapBuilder<PublicationsState>,
+): void => {
+  builder.addCase(getPublications.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getPublications.fulfilled, (state, action) => {
+    state.data = action.payload || undefined;
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getPublications.rejected, state => {
+    state.loading = 'failed';
+    // TODO to discuss manage state of failure
+  });
+};
+
+export const setSortOrderAndColumnReducer = (
+  state: PublicationsState,
+  action: PayloadAction<{ sortOrder: SortOrder; sortColumn: SortColumn }>,
+): void => {
+  state.sortColumn = action.payload.sortColumn;
+  state.sortOrder = action.payload.sortOrder;
+};
+
+export const setSelectedModelIdReducer = (
+  state: PublicationsState,
+  action: PayloadAction<string | undefined>,
+): void => {
+  state.selectedModelId = action.payload;
+};
+
+export const setSearchValueReducer = (
+  state: PublicationsState,
+  action: PayloadAction<string>,
+): void => {
+  state.searchValue = action.payload;
+};
diff --git a/src/redux/publications/publications.selectors.ts b/src/redux/publications/publications.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8ed16fac20258871aef08b11e9d2ad66b3e05923
--- /dev/null
+++ b/src/redux/publications/publications.selectors.ts
@@ -0,0 +1,53 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { ZERO } from '@/constants/common';
+import { rootSelector } from '../root/root.selectors';
+
+export const publicationsSelector = createSelector(rootSelector, state => state.publications);
+
+export const publicationsDataSelector = createSelector(
+  publicationsSelector,
+  publications => publications?.data,
+);
+
+export const publicationsListDataSelector = createSelector(
+  publicationsDataSelector,
+  data => data?.data,
+);
+
+/** totalSize is number of pages */
+export const totalSizeSelector = createSelector(publicationsDataSelector, data => data?.totalSize);
+
+export const filteredSizeSelector = createSelector(
+  publicationsDataSelector,
+  data => data?.filteredSize,
+);
+
+export const currentPageSelector = createSelector(publicationsDataSelector, data => data?.page);
+export const paginationSelector = createSelector(publicationsDataSelector, data => ({
+  pageIndex: data?.page || ZERO,
+  pageSize: 10,
+}));
+
+export const isLoadingSelector = createSelector(
+  publicationsSelector,
+  publications => publications.loading === 'pending',
+);
+
+export const sortColumnSelector = createSelector(
+  publicationsSelector,
+  publications => publications.sortColumn,
+);
+export const sortOrderSelector = createSelector(
+  publicationsSelector,
+  publications => publications.sortOrder,
+);
+
+export const selectedModelIdSelector = createSelector(
+  publicationsSelector,
+  publications => publications.selectedModelId,
+);
+
+export const searchValueSelector = createSelector(
+  publicationsSelector,
+  publications => publications.searchValue,
+);
diff --git a/src/redux/publications/publications.slice.ts b/src/redux/publications/publications.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea942b9ded0f5bf35b842a92a72b5c0c548017ed
--- /dev/null
+++ b/src/redux/publications/publications.slice.ts
@@ -0,0 +1,35 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { PublicationsState } from './publications.types';
+import {
+  getPublicationsReducer,
+  setSortOrderAndColumnReducer,
+  setSelectedModelIdReducer,
+  setSearchValueReducer,
+} from './publications.reducers';
+
+const initialState: PublicationsState = {
+  data: undefined,
+  loading: 'idle',
+  error: { name: '', message: '' },
+  sortColumn: '',
+  sortOrder: 'asc',
+  searchValue: '',
+};
+
+const publicationsSlice = createSlice({
+  name: 'publications',
+  initialState,
+  reducers: {
+    setSortOrderAndColumn: setSortOrderAndColumnReducer,
+    setSelectedModelId: setSelectedModelIdReducer,
+    setPublicationSearchValue: setSearchValueReducer,
+  },
+  extraReducers: builder => {
+    getPublicationsReducer(builder);
+  },
+});
+
+export const { setSortOrderAndColumn, setSelectedModelId, setPublicationSearchValue } =
+  publicationsSlice.actions;
+
+export default publicationsSlice.reducer;
diff --git a/src/redux/publications/publications.thunks.ts b/src/redux/publications/publications.thunks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e95a1d06a07c05963b29f22e740bab6e1e264ad4
--- /dev/null
+++ b/src/redux/publications/publications.thunks.ts
@@ -0,0 +1,18 @@
+import { createAsyncThunk } from '@reduxjs/toolkit';
+import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
+import { axiosInstance } from '@/services/api/utils/axiosInstance';
+import { PublicationsResponse } from '@/types/models';
+import { publicationsResponseSchema } from '@/models/publicationsResponseSchema';
+import { GetPublicationsParams } from './publications.types';
+import { apiPath } from '../apiPath';
+
+export const getPublications = createAsyncThunk(
+  'publications/getPublications',
+  async (params: GetPublicationsParams): Promise<PublicationsResponse | undefined> => {
+    const response = await axiosInstance.get<PublicationsResponse>(apiPath.getPublications(params));
+
+    const isDataValid = validateDataUsingZodSchema(response.data, publicationsResponseSchema);
+
+    return isDataValid ? response.data : undefined;
+  },
+);
diff --git a/src/redux/publications/publications.types.ts b/src/redux/publications/publications.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2654b800b83547b46a5b764fed866e17a52fe3dc
--- /dev/null
+++ b/src/redux/publications/publications.types.ts
@@ -0,0 +1,26 @@
+import { FetchDataState } from '@/types/fetchDataState';
+import { PublicationsResponse } from '@/types/models';
+
+export type SortColumn = '' | 'pubmedId' | 'title' | 'authors' | 'journal' | 'year' | 'level';
+export type SortOrder = 'asc' | 'desc';
+
+export type PublicationsState = FetchDataState<PublicationsResponse> & {
+  sortColumn: SortColumn;
+  sortOrder: SortOrder;
+  selectedModelId?: string;
+  searchValue: string;
+};
+
+export type PublicationsQueryParams = {
+  page?: number;
+  sortColumn?: string;
+  sortOrder?: string;
+  level?: number;
+  length?: number;
+  search?: string;
+};
+
+export type GetPublicationsParams = {
+  params: PublicationsQueryParams;
+  modelId?: string;
+};
diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts
index 8bfe3f1bcafc071a633a9da208708244fb101015..b27b88cc9c1c96511ed626d5568b30d04999b1d3 100644
--- a/src/redux/root/root.fixtures.ts
+++ b/src/redux/root/root.fixtures.ts
@@ -1,7 +1,6 @@
 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';
-import { COMPARTMENT_PATHWAYS_INITIAL_STATE_MOCK } from '../compartmentPathways/compartmentPathways.mock';
 import { CONFIGURATION_INITIAL_STATE } from '../configuration/configuration.adapter';
 import { CONTEXT_MENU_INITIAL_STATE } from '../contextMenu/contextMenu.constants';
 import { COOKIE_BANNER_INITIAL_STATE_MOCK } from '../cookieBanner/cookieBanner.mock';
@@ -18,9 +17,11 @@ import { PLUGINS_INITIAL_STATE_MOCK } from '../plugins/plugins.mock';
 import { PROJECT_STATE_INITIAL_MOCK } from '../project/project.mock';
 import { REACTIONS_STATE_INITIAL_MOCK } from '../reactions/reactions.mock';
 import { SEARCH_STATE_INITIAL_MOCK } from '../search/search.mock';
-import { STATISTICS_STATE_INITIAL_MOCK } from '../statistics/statistics.mock';
 import { RootState } from '../store';
 import { USER_INITIAL_STATE_MOCK } from '../user/user.mock';
+import { STATISTICS_STATE_INITIAL_MOCK } from '../statistics/statistics.mock';
+import { COMPARTMENT_PATHWAYS_INITIAL_STATE_MOCK } from '../compartmentPathways/compartmentPathways.mock';
+import { PUBLICATIONS_INITIAL_STATE_MOCK } from '../publications/publications.mock';
 
 export const INITIAL_STORE_STATE_MOCK: RootState = {
   search: SEARCH_STATE_INITIAL_MOCK,
@@ -43,6 +44,7 @@ export const INITIAL_STORE_STATE_MOCK: RootState = {
   legend: LEGEND_INITIAL_STATE_MOCK,
   statistics: STATISTICS_STATE_INITIAL_MOCK,
   compartmentPathways: COMPARTMENT_PATHWAYS_INITIAL_STATE_MOCK,
+  publications: PUBLICATIONS_INITIAL_STATE_MOCK,
   export: EXPORT_INITIAL_STATE_MOCK,
   plugins: PLUGINS_INITIAL_STATE_MOCK,
 };
diff --git a/src/redux/store.ts b/src/redux/store.ts
index 4e7447eba76d7e6d33cb5f70ffc8fc9b096ade8a..925fb4094256ffe3ccd792ba655e0d24e717e919 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -28,6 +28,7 @@ import legendReducer from './legend/legend.slice';
 import { mapListenerMiddleware } from './map/middleware/map.middleware';
 import pluginsReducer from './plugins/plugins.slice';
 import statisticsReducer from './statistics/statistics.slice';
+import publicationsReducer from './publications/publications.slice';
 
 export const reducers = {
   search: searchReducer,
@@ -50,6 +51,7 @@ export const reducers = {
   legend: legendReducer,
   statistics: statisticsReducer,
   compartmentPathways: compartmentPathwaysReducer,
+  publications: publicationsReducer,
   export: exportReducer,
   plugins: pluginsReducer,
 };
diff --git a/src/shared/LoadingIndicator/LoadingIndicator.component.tsx b/src/shared/LoadingIndicator/LoadingIndicator.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9f047575680c21cf2c0f6934e60960a5b02cb508
--- /dev/null
+++ b/src/shared/LoadingIndicator/LoadingIndicator.component.tsx
@@ -0,0 +1,23 @@
+import Image from 'next/image';
+import spinnerIcon from '@/assets/vectors/icons/spinner.svg';
+
+type LoadingIndicatorProps = {
+  height?: number;
+  width?: number;
+};
+
+const DEFAULT_HEIGHT = 16;
+const DEFAULT_WIDTH = 16;
+
+export const LoadingIndicator = ({
+  height = DEFAULT_HEIGHT,
+  width = DEFAULT_WIDTH,
+}: LoadingIndicatorProps): JSX.Element => (
+  <Image
+    src={spinnerIcon}
+    alt="spinner icon"
+    height={height}
+    width={width}
+    className="animate-spin"
+  />
+);
diff --git a/src/shared/LoadingIndicator/index.ts b/src/shared/LoadingIndicator/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..469606fa41b30ed3548b852599fab1cf345fe979
--- /dev/null
+++ b/src/shared/LoadingIndicator/index.ts
@@ -0,0 +1 @@
+export { LoadingIndicator } from './LoadingIndicator.component';
diff --git a/src/types/modal.ts b/src/types/modal.ts
index 8afe715f0dbd3802fd30b3b4985721003c628230..08b85048569414834c64481e4e8793335ffb0242 100644
--- a/src/types/modal.ts
+++ b/src/types/modal.ts
@@ -1 +1,7 @@
-export type ModalName = 'none' | 'overview-images' | 'mol-art' | 'login' | 'edit-overlay';
+export type ModalName =
+  | 'none'
+  | 'overview-images'
+  | 'mol-art'
+  | 'login'
+  | 'publications'
+  | 'edit-overlay';
diff --git a/src/types/models.ts b/src/types/models.ts
index 293fd8ffe08e9930c8d2b88385a697b16834106e..3f9efc0e75c9c1260b4572e642240f24c7b471a2 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -1,3 +1,4 @@
+import { z } from 'zod';
 import { bioEntityContentSchema } from '@/models/bioEntityContentSchema';
 import { bioEntityResponseSchema } from '@/models/bioEntityResponseSchema';
 import { bioEntitySchema } from '@/models/bioEntitySchema';
@@ -39,13 +40,14 @@ import {
 import { overviewImageView } from '@/models/overviewImageView';
 import { pluginSchema } from '@/models/pluginSchema';
 import { projectSchema } from '@/models/projectSchema';
+import { publicationSchema } from '@/models/publicationsSchema';
 import { reactionSchema } from '@/models/reaction';
 import { reactionLineSchema } from '@/models/reactionLineSchema';
 import { referenceSchema } from '@/models/referenceSchema';
 import { sessionSchemaValid } from '@/models/sessionValidSchema';
 import { statisticsSchema } from '@/models/statisticsSchema';
 import { targetSchema } from '@/models/targetSchema';
-import { z } from 'zod';
+import { publicationsResponseSchema } from '@/models/publicationsResponseSchema';
 
 export type Project = z.infer<typeof projectSchema>;
 export type OverviewImageView = z.infer<typeof overviewImageView>;
@@ -87,6 +89,8 @@ export type Color = z.infer<typeof colorSchema>;
 export type Statistics = z.infer<typeof statisticsSchema>;
 export type CompartmentPathway = z.infer<typeof compartmentPathwaySchema>;
 export type CompartmentPathwayDetails = z.infer<typeof compartmentPathwayDetailsSchema>;
+export type PublicationsResponse = z.infer<typeof publicationsResponseSchema>;
+export type Publication = z.infer<typeof publicationSchema>;
 export type ExportNetwork = z.infer<typeof exportNetworkchema>;
 export type ExportElements = z.infer<typeof exportElementsSchema>;
 export type MinervaPlugin = z.infer<typeof pluginSchema>; // Plugin type interfers with global Plugin type
diff --git a/tailwind.config.ts b/tailwind.config.ts
index a41549602c07c850668d40ae4b784fafdfb237bc..0f75c8f70c01a9fc34cd4d0a7b4e4c4605fb3c36 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -23,6 +23,7 @@ const config: Config = {
         cinnabar: '#ec4d2c',
         'med-sea-green': '#3ab65d',
         cultured: '#f7f7f8',
+        lotion: '#fafafa',
         'white-pearl': '#ffffff',
         divide: '#e1e0e6',
         orange: '#f48c40',