diff --git a/.vscode/settings.json b/.vscode/settings.json
index a0240964a..bb8e0fdb0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -5,18 +5,21 @@
     "editor.formatOnSave": false,
     "editor.codeActionsOnSave": {
       "source.fixAll.eslint": true,
+      "source.addMissingImports": "always"
     }
   },
   "[javascript]": {
     "editor.formatOnSave": false,
     "editor.codeActionsOnSave": {
       "source.fixAll.eslint": true,
+      "source.addMissingImports": "always"
     }
   },
   "[typescriptreact]": {
     "editor.formatOnSave": false,
     "editor.codeActionsOnSave": {
       "source.fixAll.eslint": true,
+      "source.addMissingImports": "always"
     }
   },
   "[json]": {
diff --git a/front/src/App.tsx b/front/src/App.tsx
index afa91e55d..9819a65ec 100644
--- a/front/src/App.tsx
+++ b/front/src/App.tsx
@@ -28,6 +28,8 @@ import { SettingsWorkspaceMembers } from '~/pages/settings/SettingsWorkspaceMemb
 import { Tasks } from '~/pages/tasks/Tasks';
 import { getPageTitleFromPath } from '~/utils/title-utils';
 
+import { ObjectTablePage } from './pages/companies/ObjectsTable';
+
 export const App = () => {
   const { pathname } = useLocation();
   const pageTitle = getPageTitleFromPath(pathname);
@@ -54,6 +56,16 @@ export const App = () => {
           } />
 
           } />
+          
+            }
+          />
+
           ,
   ];
+
+export const suppliersAvailableColumnDefinitions: ColumnDefinition[] =
+  [
+    {
+      key: 'name',
+      name: 'Name',
+      Icon: IconBuildingSkyscraper,
+      size: 180,
+      index: 0,
+      type: 'text',
+      metadata: {
+        fieldName: 'name',
+        placeHolder: 'Company Name',
+      },
+      isVisible: true,
+      buttonIcon: IconArrowUpRight,
+      infoTooltipContent: 'The company name.',
+      basePathToShowPage: '/companies/',
+    } satisfies ColumnDefinition,
+    {
+      key: 'city',
+      name: 'City',
+      Icon: IconBuildingSkyscraper,
+      size: 180,
+      index: 0,
+      type: 'text',
+      metadata: {
+        fieldName: 'city',
+        placeHolder: 'Company Name',
+      },
+      isVisible: true,
+      buttonIcon: IconArrowUpRight,
+      infoTooltipContent: 'The company name.',
+      basePathToShowPage: '/companies/',
+    } satisfies ColumnDefinition,
+  ];
diff --git a/front/src/modules/metadata/components/FetchMetadataEffect.tsx b/front/src/modules/metadata/components/FetchMetadataEffect.tsx
index 698c82168..d4ff6b642 100644
--- a/front/src/modules/metadata/components/FetchMetadataEffect.tsx
+++ b/front/src/modules/metadata/components/FetchMetadataEffect.tsx
@@ -21,7 +21,10 @@ export const FetchMetadataEffect = () => {
           query: GET_ALL_OBJECTS,
         });
 
-        if (objects.data.objects.edges.length > 0) {
+        if (
+          objects.data.objects.edges.length > 0 &&
+          metadataObjects.length === 0
+        ) {
           const formattedObjects: MetadataObject[] =
             objects.data.objects.edges.map((object) => ({
               ...object.node,
diff --git a/front/src/modules/metadata/components/ObjectDataTableEffect.tsx b/front/src/modules/metadata/components/ObjectDataTableEffect.tsx
new file mode 100644
index 000000000..3badecf63
--- /dev/null
+++ b/front/src/modules/metadata/components/ObjectDataTableEffect.tsx
@@ -0,0 +1,68 @@
+import { useEffect } from 'react';
+import { useSearchParams } from 'react-router-dom';
+import { useRecoilCallback } from 'recoil';
+
+import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
+import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
+import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
+import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
+import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
+import { savedSortsFamilyState } from '@/ui/view-bar/states/savedSortsFamilyState';
+import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
+
+import { useFindManyCustomObjects } from '../hooks/useFindManyCustomObjects';
+
+import { useSetObjectDataTableData } from './useSetDataTableData';
+
+export const ObjectDataTableEffect = ({
+  objectName,
+  objectNameSingular,
+}: {
+  objectNameSingular: string;
+  objectName: string;
+}) => {
+  const setDataTableData = useSetObjectDataTableData();
+
+  const { data } = useFindManyCustomObjects({ objectName });
+
+  useEffect(() => {
+    const entities = data?.['findMany' + objectNameSingular]?.edges ?? [];
+
+    setDataTableData(entities);
+  }, [data, objectNameSingular, setDataTableData]);
+
+  const [searchParams] = useSearchParams();
+  const tableRecoilScopeId = useRecoilScopeId(TableRecoilScopeContext);
+  const handleViewSelect = useRecoilCallback(
+    ({ set, snapshot }) =>
+      async (viewId: string) => {
+        const currentView = await snapshot.getPromise(
+          currentViewIdScopedState(tableRecoilScopeId),
+        );
+        if (currentView === viewId) {
+          return;
+        }
+
+        const savedFilters = await snapshot.getPromise(
+          savedFiltersFamilyState(viewId),
+        );
+        const savedSorts = await snapshot.getPromise(
+          savedSortsFamilyState(viewId),
+        );
+
+        set(filtersScopedState(tableRecoilScopeId), savedFilters);
+        set(sortsScopedState(tableRecoilScopeId), savedSorts);
+        set(currentViewIdScopedState(tableRecoilScopeId), viewId);
+      },
+    [tableRecoilScopeId],
+  );
+
+  useEffect(() => {
+    const viewId = searchParams.get('view');
+    if (viewId) {
+      handleViewSelect(viewId);
+    }
+  }, [handleViewSelect, searchParams]);
+
+  return <>>;
+};
diff --git a/front/src/modules/metadata/components/ObjectTable.tsx b/front/src/modules/metadata/components/ObjectTable.tsx
new file mode 100644
index 000000000..04bd98a34
--- /dev/null
+++ b/front/src/modules/metadata/components/ObjectTable.tsx
@@ -0,0 +1,57 @@
+import { suppliersAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions';
+import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport';
+import { DataTable } from '@/ui/data-table/components/DataTable';
+import { TableContext } from '@/ui/data-table/contexts/TableContext';
+import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
+import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
+import { useTableViews } from '@/views/hooks/useTableViews';
+
+import { ObjectDataTableEffect } from './ObjectDataTableEffect';
+
+export const ObjectTable = ({
+  objectName,
+  objectNameSingular,
+}: {
+  objectNameSingular: string;
+  objectName: string;
+}) => {
+  const { createView, deleteView, submitCurrentView, updateView } =
+    useTableViews({
+      objectId: 'company',
+      columnDefinitions: suppliersAvailableColumnDefinitions,
+    });
+
+  const { openCompanySpreadsheetImport } = useSpreadsheetCompanyImport();
+
+  return (
+     {
+          //
+        },
+      }}
+    >
+      
+      
+         {
+            //
+          }}
+        />
+      
+    
+  );
+};
diff --git a/front/src/modules/metadata/components/useSetDataTableData.ts b/front/src/modules/metadata/components/useSetDataTableData.ts
new file mode 100644
index 000000000..17025876b
--- /dev/null
+++ b/front/src/modules/metadata/components/useSetDataTableData.ts
@@ -0,0 +1,61 @@
+import { useRecoilCallback } from 'recoil';
+
+import { useResetTableRowSelection } from '@/ui/data-table/hooks/useResetTableRowSelection';
+import { isFetchingDataTableDataState } from '@/ui/data-table/states/isFetchingDataTableDataState';
+import { numberOfTableRowsState } from '@/ui/data-table/states/numberOfTableRowsState';
+import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
+import { tableRowIdsState } from '@/ui/data-table/states/tableRowIdsState';
+import { entityFieldsFamilyState } from '@/ui/field/states/entityFieldsFamilyState';
+import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
+import { availableFiltersScopedState } from '@/ui/view-bar/states/availableFiltersScopedState';
+import { availableSortsScopedState } from '@/ui/view-bar/states/availableSortsScopedState';
+import { entityCountInCurrentViewState } from '@/ui/view-bar/states/entityCountInCurrentViewState';
+
+export const useSetObjectDataTableData = () => {
+  const resetTableRowSelection = useResetTableRowSelection();
+
+  const tableContextScopeId = useRecoilScopeId(TableRecoilScopeContext);
+
+  return useRecoilCallback(
+    ({ set, snapshot }) =>
+      (newEntityArrayRaw: T[]) => {
+        const newEntityArray = newEntityArrayRaw.map((entity) => entity.node);
+
+        for (const entity of newEntityArray) {
+          const currentEntity = snapshot
+            .getLoadable(entityFieldsFamilyState(entity.id))
+            .valueOrThrow();
+
+          if (JSON.stringify(currentEntity) !== JSON.stringify(entity)) {
+            set(entityFieldsFamilyState(entity.id), entity);
+          }
+        }
+
+        const entityIds = newEntityArray.map((entity) => entity.id);
+
+        // eslint-disable-next-line no-console
+        console.log({ newEntityArray, entityIds });
+
+        set(tableRowIdsState, (currentRowIds) => {
+          if (JSON.stringify(currentRowIds) !== JSON.stringify(entityIds)) {
+            return entityIds;
+          }
+
+          return currentRowIds;
+        });
+
+        resetTableRowSelection();
+
+        set(numberOfTableRowsState, entityIds.length);
+
+        set(entityCountInCurrentViewState, entityIds.length);
+
+        set(availableFiltersScopedState(tableContextScopeId), []);
+
+        set(availableSortsScopedState(tableContextScopeId), []);
+
+        set(isFetchingDataTableDataState, false);
+      },
+    [resetTableRowSelection, tableContextScopeId],
+  );
+};
diff --git a/front/src/modules/metadata/hooks/useCreateOneCustomObject.ts b/front/src/modules/metadata/hooks/useCreateOneCustomObject.ts
new file mode 100644
index 000000000..8d6517734
--- /dev/null
+++ b/front/src/modules/metadata/hooks/useCreateOneCustomObject.ts
@@ -0,0 +1,8 @@
+// TODO: add zod to validate that we have at least id on each object
+export const useCreateOneCustomObject = ({
+  _objectName,
+}: {
+  _objectName: string;
+}) => {
+  // TODO : code
+};
diff --git a/front/src/modules/metadata/hooks/useFindManyCustomObjects.ts b/front/src/modules/metadata/hooks/useFindManyCustomObjects.ts
new file mode 100644
index 000000000..497a434d9
--- /dev/null
+++ b/front/src/modules/metadata/hooks/useFindManyCustomObjects.ts
@@ -0,0 +1,59 @@
+import { gql, useQuery } from '@apollo/client';
+import { useRecoilState } from 'recoil';
+
+import { metadataObjectsState } from '../states/metadataObjectsState';
+import { generateFindManyCustomObjectsQuery } from '../utils/generateFindManyCustomObjectsQuery';
+
+// TODO: add zod to validate that we have at least id on each object
+export const useFindManyCustomObjects = ({
+  objectName,
+}: {
+  objectName: string;
+}) => {
+  const [metadataObjects] = useRecoilState(metadataObjectsState);
+
+  const foundObject = metadataObjects.find(
+    (object) => object.nameSingular === objectName,
+  );
+
+  // eslint-disable-next-line no-console
+  console.log({ foundObject });
+
+  const generatedQuery = foundObject
+    ? generateFindManyCustomObjectsQuery({
+        metadataObject: foundObject,
+      })
+    : gql`
+        query EmptyQuery {
+          empty
+        }
+      `;
+
+  const {
+    fetchMore: fetchMoreBase,
+    data,
+    loading,
+    error,
+  } = useQuery(generatedQuery, {
+    skip: !foundObject,
+  });
+
+  // eslint-disable-next-line no-console
+  console.log({ data, loading, error });
+
+  const fetchMore = ({ fromCursor }: { fromCursor: string }) => {
+    fetchMoreBase({
+      variables: { fromCursor },
+    });
+  };
+
+  const objectNotFoundInMetadata = metadataObjects.length > 0 && !foundObject;
+
+  return {
+    data,
+    loading,
+    error,
+    fetchMore,
+    objectNotFoundInMetadata,
+  };
+};
diff --git a/front/src/modules/metadata/utils/generateFindManyCustomObjectsQuery.ts b/front/src/modules/metadata/utils/generateFindManyCustomObjectsQuery.ts
new file mode 100644
index 000000000..b1308f127
--- /dev/null
+++ b/front/src/modules/metadata/utils/generateFindManyCustomObjectsQuery.ts
@@ -0,0 +1,27 @@
+import { gql } from '@apollo/client';
+
+import { MetadataObject } from '../types/MetadataObject';
+
+export const generateFindManyCustomObjectsQuery = ({
+  metadataObject,
+  _fromCursor,
+}: {
+  metadataObject: MetadataObject;
+  _fromCursor?: string;
+}) => {
+  return gql`
+    query CustomQuery${metadataObject.nameSingular} {
+      findMany${metadataObject.nameSingular}{
+        edges {
+          node {
+            id
+            ${metadataObject.fields
+              .map((field) => field.nameSingular)
+              .join('\n')}
+          }
+          cursor
+        }
+      }
+    }
+  `;
+};
diff --git a/front/src/modules/types/AppPath.ts b/front/src/modules/types/AppPath.ts
index f881040ec..51e7af629 100644
--- a/front/src/modules/types/AppPath.ts
+++ b/front/src/modules/types/AppPath.ts
@@ -17,6 +17,8 @@ export enum AppPath {
   PersonShowPage = '/person/:personId',
   TasksPage = '/tasks',
   OpportunitiesPage = '/opportunities',
+  ObjectTablePage = '/:objectName',
+
   SettingsCatchAll = `/settings/*`,
 
   // Impersonate
diff --git a/front/src/modules/ui/data-table/components/DataTableBody.tsx b/front/src/modules/ui/data-table/components/DataTableBody.tsx
index 62af6f4ee..2467762cb 100644
--- a/front/src/modules/ui/data-table/components/DataTableBody.tsx
+++ b/front/src/modules/ui/data-table/components/DataTableBody.tsx
@@ -27,6 +27,9 @@ export const DataTableBody = () => {
 
   const tableRowIds = useRecoilValue(tableRowIdsState);
 
+  // eslint-disable-next-line no-console
+  console.log({ tableRowIds });
+
   const isNavbarSwitchingSize = useRecoilValue(isNavbarSwitchingSizeState);
   const isFetchingDataTableData = useRecoilValue(isFetchingDataTableDataState);
 
diff --git a/front/src/modules/ui/data-table/components/DataTableCell.tsx b/front/src/modules/ui/data-table/components/DataTableCell.tsx
index 7025e7480..b7086f70e 100644
--- a/front/src/modules/ui/data-table/components/DataTableCell.tsx
+++ b/front/src/modules/ui/data-table/components/DataTableCell.tsx
@@ -37,6 +37,9 @@ export const DataTableCell = ({ cellIndex }: { cellIndex: number }) => {
 
   const updateEntityMutation = useContext(EntityUpdateMutationContext);
 
+  // eslint-disable-next-line no-console
+  console.log({ columnDefinition, currentRowId });
+
   if (!columnDefinition || !currentRowId) {
     return null;
   }
diff --git a/front/src/modules/ui/data-table/components/DataTableRow.tsx b/front/src/modules/ui/data-table/components/DataTableRow.tsx
index 0515d6bc0..d6f8eef9d 100644
--- a/front/src/modules/ui/data-table/components/DataTableRow.tsx
+++ b/front/src/modules/ui/data-table/components/DataTableRow.tsx
@@ -27,7 +27,8 @@ export const DataTableRow = forwardRef(
       TableRecoilScopeContext,
     );
     const { currentRowSelected } = useCurrentRowSelected();
-
+    // eslint-disable-next-line no-console
+    console.log({ visibleTableColumns });
     return (
        {
   const { fieldDefinition } = useContext(FieldContext);
 
+  // eslint-disable-next-line no-console
+  console.log({ fieldDefinition });
+
   const { closeTableCell } = useTableCell();
 
   const { moveLeft, moveRight, moveDown } = useMoveSoftFocus();
diff --git a/front/src/pages/companies/ObjectsTable.tsx b/front/src/pages/companies/ObjectsTable.tsx
new file mode 100644
index 000000000..75f909e39
--- /dev/null
+++ b/front/src/pages/companies/ObjectsTable.tsx
@@ -0,0 +1,58 @@
+import styled from '@emotion/styled';
+import { v4 } from 'uuid';
+
+import { ObjectTable } from '@/metadata/components/ObjectTable';
+import { DataTableActionBar } from '@/ui/data-table/action-bar/components/DataTableActionBar';
+import { DataTableContextMenu } from '@/ui/data-table/context-menu/components/DataTableContextMenu';
+import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
+import { IconBuildingSkyscraper } from '@/ui/icon';
+import { PageAddButton } from '@/ui/layout/components/PageAddButton';
+import { PageBody } from '@/ui/layout/components/PageBody';
+import { PageContainer } from '@/ui/layout/components/PageContainer';
+import { PageHeader } from '@/ui/layout/components/PageHeader';
+import { PageHotkeysEffect } from '@/ui/layout/components/PageHotkeysEffect';
+import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
+
+const StyledTableContainer = styled.div`
+  display: flex;
+  width: 100%;
+`;
+
+export const ObjectTablePage = ({
+  objectName,
+  objectNameSingular,
+}: {
+  objectNameSingular: string;
+  objectName: string;
+}) => {
+  const handleAddButtonClick = async () => {
+    const newCompanyId: string = v4();
+
+    // eslint-disable-next-line no-console
+    console.log('newCompanyId', newCompanyId);
+  };
+
+  return (
+    
+      
+        
+        
+      
+      
+        
+          
+            
+          
+          
+          
+        
+      
+    
+  );
+};