diff --git a/.gitignore b/.gitignore
index 6411dbf0d520a9f0a126616ca9a00a2b86418394..7749c1bd20bd40fb0e1d5746687a0dd15eabc63e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,6 +80,7 @@ node_modules
 tsconfig.tsbuildinfo
 vite.config.ts.*
 
+
 # Generated ts definitions files for CSS modules
 *.d.ts
 *.module.scss.d.ts
diff --git a/apps/web/src/app/app.tsx b/apps/web/src/app/app.tsx
index ea709980a0e9feeba1d51ccc26839f8b9921cd8a..90d3c2259615ceebb30f7bc0ed4674d03180164f 100644
--- a/apps/web/src/app/app.tsx
+++ b/apps/web/src/app/app.tsx
@@ -11,9 +11,12 @@ import Onboarding from '../components/onboarding/onboarding';
 import { wsQueryRequest } from '@graphpolaris/shared/lib/data-access/broker';
 import { URLParams, setParam } from '@graphpolaris/shared/lib/data-access/api/url';
 
-const Schema = React.lazy(() => import('@graphpolaris/shared/lib/schema/panel'));
-const VisualizationPanel = React.lazy(() => import('@graphpolaris/shared/lib/vis/panel'));
-const QueryBuilder = React.lazy(() => import('@graphpolaris/shared/lib/querybuilder'));
+import { Schema } from '@graphpolaris/shared/lib/schema/panel';
+import { VisualizationPanel } from '@graphpolaris/shared/lib/vis';
+import { QueryBuilder } from '@graphpolaris/shared/lib/querybuilder';
+// const Schema = React.lazy(() => import('@graphpolaris/shared/lib/schema/panel'));
+// const VisualizationPanel = React.lazy(() => import('@graphpolaris/shared/lib/vis'));
+// const QueryBuilder = React.lazy(() => import('@graphpolaris/shared/lib/querybuilder'));
 
 export type App = {
   load?: string;
diff --git a/apps/web/src/app/panels/VisualizationDialog.tsx b/apps/web/src/app/panels/VisualizationDialog.tsx
deleted file mode 100644
index 805d201b1d6ee9df7800e286966b741853bef749..0000000000000000000000000000000000000000
--- a/apps/web/src/app/panels/VisualizationDialog.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { FormActions, FormBody, FormCard, FormControl, FormDiv, FormHBar, FormTitle } from '@graphpolaris/shared/lib/components/forms';
-import { DialogProps } from '@graphpolaris/shared/lib/components/Dialog';
-import { useAppDispatch, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
-import { updateGeneralSettings, updateVisualizationSettings } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
-import Input from '@graphpolaris/shared/lib/components/inputs';
-import { globalConfigSchemaTypes, localConfigSchemaType } from '@graphpolaris/shared/lib/vis/types';
-
-export default function VisualizationDialog(props: DialogProps) {
-  const dispatch = useAppDispatch();
-  const vis = useVisualizationState();
-  const [settingsGeneral, setSettingsGeneral] = useState<globalConfigSchemaTypes>(vis.settings.general);
-  const [settingsSpecific, setSettingsSpecific] = useState<localConfigSchemaType>(vis.settings[vis.activeVisualization]);
-
-  useEffect(() => {
-    if (vis.settings.general !== settingsGeneral) {
-      dispatch(updateGeneralSettings(settingsGeneral));
-    }
-  }, [settingsGeneral]);
-
-  useEffect(() => {
-    if (vis.settings[vis.activeVisualization] !== settingsSpecific) {
-      dispatch(
-        updateVisualizationSettings({
-          id: vis.activeVisualization,
-          settings: settingsSpecific,
-        }),
-      );
-    }
-  }, [settingsSpecific]);
-
-  useEffect(() => {
-    setSettingsSpecific(vis.settings[vis.activeVisualization]);
-  }, [vis.settings[vis.activeVisualization]]);
-
-  return (
-    <>
-      {props.open && (
-        <FormDiv>
-          <FormCard>
-            <FormBody
-              onSubmit={(e) => {
-                e.preventDefault();
-                props.onClose();
-              }}
-            >
-              <FormTitle title="Settings" onClose={props.onClose} />
-              <FormHBar />
-
-              {settingsGeneral && (
-                <>
-                  <p className="font-sm font-bold pl-2">General</p>
-                  {Object.keys(settingsGeneral).map((val) => (
-                    <FormControl key={val}>
-                      <Input
-                        {...settingsGeneral[val]}
-                        value={vis.settings.general[val].value as any}
-                        onChange={(value: any) => {
-                          setSettingsGeneral((prevSettings) => ({
-                            ...prevSettings,
-                            [val]: { ...prevSettings[val], value: value },
-                          }));
-                        }}
-                      />
-                    </FormControl>
-                  ))}
-                </>
-              )}
-
-              {settingsSpecific && (
-                <>
-                  <FormHBar />
-                  <p className="font-sm font-bold pl-2">Specific</p>
-                  {Object.keys(settingsSpecific).map((val) => {
-                    const currentSetting = settingsSpecific[val];
-                    const shouldShowSetting = currentSetting.condition ? currentSetting.condition?.(settingsSpecific) : true;
-                    console.log(currentSetting, shouldShowSetting);
-                    return (
-                      shouldShowSetting && (
-                        <FormControl key={val}>
-                          <Input
-                            {...settingsSpecific[val]}
-                            value={settingsSpecific[val]?.value as any}
-                            onChange={(value: any) => {
-                              setSettingsSpecific((prevSettings) => ({
-                                ...prevSettings,
-                                [val]: { ...prevSettings[val], value: value },
-                              }));
-                            }}
-                          />
-                        </FormControl>
-                      )
-                    );
-                  })}
-                </>
-              )}
-
-              <FormHBar />
-              <FormActions onClose={props.onClose} />
-            </FormBody>
-          </FormCard>
-        </FormDiv>
-      )}
-    </>
-  );
-}
diff --git a/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
index e15109c95451f9eb42db78fbf0fb38900194c995..820d0d462c994d867f15d0e04355482f3f6afad0 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
@@ -19,7 +19,7 @@ export default function DatabaseSelector({}) {
   const [hovered, setHovered] = useState<string | null>(null);
   const [connecting, setConnecting] = useState<boolean>(false);
   const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false);
-  const [settingsMenuOpen, setSettingsMenuOpen] = useState<'create' | 'update' | undefined>(undefined);
+  const [settingsMenuOpen, setSettingsMenuOpen] = useState<'add' | 'update' | undefined>(undefined);
   const [selectedSaveState, setSelectedSaveState] = useState<SaveStateI | null>(null);
   // const [addDbConnectionFormOpen, setAddDbConnectionFormOpen] = useState<boolean>(false);
 
@@ -31,7 +31,7 @@ export default function DatabaseSelector({}) {
         settingsMenuOpen === undefined) ||
       session.currentSaveState === nilUUID
     ) {
-      setSettingsMenuOpen('create');
+      setSettingsMenuOpen('add');
     }
   }, [session, settingsMenuOpen]);
 
@@ -120,7 +120,7 @@ export default function DatabaseSelector({}) {
             </div>
           }
           onClick={() => {
-            if (session.saveStates && Object.keys(session.saveStates).length === 0) setSettingsMenuOpen('create');
+            if (session.saveStates && Object.keys(session.saveStates).length === 0) setSettingsMenuOpen('add');
             else setDbSelectionMenuOpen(!dbSelectionMenuOpen);
           }}
         />
@@ -133,7 +133,7 @@ export default function DatabaseSelector({}) {
                 e.preventDefault();
                 setDbSelectionMenuOpen(false);
                 setConnecting(false);
-                setSettingsMenuOpen('create');
+                setSettingsMenuOpen('add');
               }}
               title="Add new database"
             >
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
index 31f59a59a8c9d57051c74ca28f9b4723dea04ffc..8a11c9ab9add17f74c82140589de367c488f40b7 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
@@ -50,7 +50,7 @@ export const DatabaseForm = (props: { data: SaveStateI; onChange: (data: SaveSta
     <>
       <Input
         type="text"
-        label="Name of database"
+        label="Name of database connection"
         value={formData.name}
         onChange={(value: string) =>
           setFormData((draft) => {
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
index fe477e31aa11f0c3cadc40c79171203985d3ae3c..eef51b9ed6db2de71a7e463ce97a1a9cd4feca93 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
@@ -1,22 +1,16 @@
-import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import {
-  databaseNameMapping,
-  databaseProtocolMapping,
   useAppDispatch,
   SaveStateI,
   wsUpdateState,
-  DatabaseStatus,
   wsTestDatabaseConnection,
   wsCreateState,
-  nilUUID,
-  DatabaseType,
   useAuthorizationCache,
 } from '@graphpolaris/shared/lib/data-access';
 import { ErrorOutline } from '@mui/icons-material';
 import { Dialog } from '@graphpolaris/shared/lib/components/Dialog';
 import { Button } from '@graphpolaris/shared/lib/components/buttons';
 import { useImmer } from 'use-immer';
-import { Broker } from '@graphpolaris/shared/lib/data-access/broker/broker';
 import { addSaveState, testedSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice';
 import { DatabaseForm, INITIAL_SAVE_STATE } from './databaseForm';
 import { SampleDatabaseSelector } from './mockSaveStates';
@@ -27,12 +21,7 @@ type Connection = {
   verified: boolean | null;
 };
 
-export const SettingsForm = (props: {
-  onClose(): void;
-  open: 'create' | 'update';
-  saveState: SaveStateI | null;
-  disableCancel?: boolean;
-}) => {
+export const SettingsForm = (props: { onClose(): void; open: 'add' | 'update'; saveState: SaveStateI | null; disableCancel?: boolean }) => {
   const dispatch = useAppDispatch();
   const ref = useRef<HTMLDialogElement>(null);
   const auth = useAuthorizationCache();
@@ -46,7 +35,7 @@ export const SettingsForm = (props: {
     status: null,
     verified: null,
   });
-  const formTitle = props.open === 'create' ? 'Create' : 'Update';
+  const formTitle = props.open === 'add' ? 'Add' : 'Update';
 
   useEffect(() => {
     if (props.saveState && props.open === 'update') {
@@ -57,21 +46,22 @@ export const SettingsForm = (props: {
     }
   }, [props.saveState]);
 
-  async function handleSubmit() {
+  async function handleSubmit(saveStateData?: SaveStateI) {
+    if (!saveStateData) saveStateData = formData;
     setConnection(() => ({
       updating: true,
-      status: formTitle.slice(0, -1) + 'ing database credentials',
+      status: 'Testing database connection',
       verified: null,
     }));
 
-    wsTestDatabaseConnection(formData.db, (data) => {
-      if (!formData) {
+    wsTestDatabaseConnection(saveStateData.db, (data) => {
+      if (!saveStateData) {
         console.error('formData is null');
         return;
       }
-      if (formData.user_id !== auth.userID && auth.userID) {
-        console.error('formData.user_id is not equal to auth.userID');
-        formData.user_id = auth.userID;
+      if (saveStateData.user_id !== auth.userID && auth.userID) {
+        console.error('user_id is not equal to auth.userID');
+        saveStateData.user_id = auth.userID;
       }
       if (data && data.status === 'success') {
         setConnection((prevState) => ({
@@ -79,15 +69,15 @@ export const SettingsForm = (props: {
           status: 'Database connection verified',
           verified: true,
         }));
-        if (props.open === 'create') {
-          wsCreateState(formData, (_data) => {
+        if (props.open === 'add') {
+          wsCreateState(saveStateData, (_data) => {
             dispatch(addSaveState(_data));
             dispatch(testedSaveState(_data.id));
             closeDialog();
           });
         } else {
           dispatch(testedSaveState(data.saveStateID));
-          wsUpdateState(formData, (_data) => {
+          wsUpdateState(saveStateData, (_data) => {
             dispatch(addSaveState(_data));
             closeDialog();
           });
@@ -95,7 +85,7 @@ export const SettingsForm = (props: {
       } else {
         setConnection((prevState) => ({
           updating: false,
-          status: 'Database connection failed',
+          status: 'Database connection test failed',
           verified: false,
         }));
       }
@@ -124,7 +114,7 @@ export const SettingsForm = (props: {
   return (
     <Dialog open={!!props.open} onClose={props.onClose} className="lg:min-w-[50rem]">
       <div className="flex justify-between align-center">
-        <h1 className="text-xl font-bold">{formTitle} Database</h1>
+        <h1 className="text-xl font-bold">{formTitle} Database Connection</h1>
         <div>
           {sampleDataPanel === true ? (
             <Button variant="outline" label="Go back" onClick={() => setSampleDataPanel(false)} />
@@ -145,9 +135,8 @@ export const SettingsForm = (props: {
         {sampleDataPanel === true ? (
           <SampleDatabaseSelector
             onClick={(data) => {
-              setFormData({ ...data, user_id: auth.userID || '' });
               setHasError(false);
-              handleSubmit();
+              handleSubmit({ ...data, user_id: auth.userID || '' });
             }}
           />
         ) : (
diff --git a/libs/shared/lib/components/charts/barplot/index.tsx b/libs/shared/lib/components/charts/barplot/index.tsx
index 362918122337a1ec52f69ae31a83144395d8fd81..034b5f1f6730044b1c26f4bff9ff6e21916249f6 100644
--- a/libs/shared/lib/components/charts/barplot/index.tsx
+++ b/libs/shared/lib/components/charts/barplot/index.tsx
@@ -1,6 +1,6 @@
 import React, { LegacyRef, useEffect, useRef, useState } from 'react';
 import * as d3 from 'd3';
-import Tooltip, { TooltipProps } from '@graphpolaris/shared/lib/components/tooltip';
+import { BarplotTooltip } from '@graphpolaris/shared/lib/components/tooltip';
 
 export type BarPlotProps = {
   data: { category: string; count: number }[];
@@ -222,7 +222,7 @@ export const BarPlot = ({ typeBarPlot: typeBarplot, numBins, data }: BarPlotProp
         preserveAspectRatio="xMidYMid meet"
         viewBox={`0 0 ${dimensions.width} ${dimensions.height}`}
       ></svg>
-      {tooltipData && <Tooltip x={tooltipData.x} y={tooltipData.y} content={tooltipData.content} />}
+      {tooltipData && <BarplotTooltip x={tooltipData.x} y={tooltipData.y} content={tooltipData.content} />}
     </div>
   );
 };
diff --git a/libs/shared/lib/components/colorComponents/colorDropdown/colorLegendDropdown.stories.tsx b/libs/shared/lib/components/colorComponents/colorDropdown/colorLegendDropdown.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1f88c8e20f04267053002784c40450eaaab561d0
--- /dev/null
+++ b/libs/shared/lib/components/colorComponents/colorDropdown/colorLegendDropdown.stories.tsx
@@ -0,0 +1,22 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import DropdownColorLegend from '.';
+
+const Component: Meta<typeof DropdownColorLegend> = {
+  title: 'ColorManager/Legend/DropdownGP',
+  component: DropdownColorLegend,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const DropdownColorLegendStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+
+  return <DropdownColorLegend value={value} onChange={setValue} />;
+};
+
+DropdownColorLegendStory.args = {};
diff --git a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..41d3ce9caac5c52363581095c5a92a4d61fb9955
--- /dev/null
+++ b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx
@@ -0,0 +1,100 @@
+import React, { useState } from 'react';
+import * as d3 from 'd3';
+import { dataColors } from 'config/src/colors.js';
+import { DropdownButton, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
+import ColorLegend from '../colorLegend/index.js';
+
+type TailwindColor = {
+  colors: string | { [key: number]: string };
+  data: { min: number; max: number };
+  name: string;
+  showAxis: boolean;
+};
+
+type TailwindColors = {
+  [key: string]: TailwindColor;
+};
+
+function generateTailwindColors(dataColors: any) {
+  const tailwindColors: TailwindColors = {};
+
+  for (const colorName in dataColors) {
+    let colorData = dataColors[colorName];
+
+    if (colorName !== 'black' && colorName !== 'white') {
+      tailwindColors[colorName] = {
+        colors: colorData,
+        data: { min: 0, max: 100 },
+        name: `seq:${colorName}`,
+        showAxis: false,
+      };
+    }
+  }
+
+  return tailwindColors;
+}
+
+type DropdownColorLegendProps = {
+  value: any;
+  onChange: (val: any) => void;
+};
+
+export const DropdownColorLegend = ({ value, onChange }: DropdownColorLegendProps) => {
+  const colorStructure = generateTailwindColors(dataColors);
+  const [selectedColorLegend, setSelectedColorLegend] = useState<any>(null);
+
+  const [menuOpen, setMenuOpen] = useState<boolean>(false);
+  const [selectedOption, setSelectedOption] = useState<any>('Select colormap');
+
+  const handleButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
+    e.preventDefault();
+    setMenuOpen(!menuOpen);
+  };
+
+  const handleOptionClick = (option: string) => {
+    setSelectedOption(option);
+    setSelectedColorLegend(colorStructure[option]);
+    setMenuOpen(false);
+  };
+
+  return (
+    <div className="w-200 h-200">
+      <DropdownContainer className="w-60">
+        <DropdownButton
+          title={
+            <div className="flex items-center h-4">
+              {selectedColorLegend ? (
+                <ColorLegend
+                  colors={selectedColorLegend.colors}
+                  data={selectedColorLegend.data}
+                  name={selectedColorLegend.name}
+                  showAxis={selectedColorLegend.showAxis}
+                />
+              ) : (
+                <p className="ml-2">{selectedOption}</p>
+              )}
+            </div>
+          }
+          onClick={handleButtonClick}
+        />
+        {menuOpen && (
+          <DropdownItemContainer align="top-10" className="w-60">
+            {Object.keys(colorStructure).map((option: any, index) => (
+              <li key={index} onClick={() => handleOptionClick(option)} className="cursor-pointer flex items-center ml-2 h-4 m-2">
+                <ColorLegend
+                  key={index.toString() + '_colorLegend'}
+                  colors={colorStructure[option].colors}
+                  data={colorStructure[option].data}
+                  name={colorStructure[option].name}
+                  showAxis={colorStructure[option].showAxis}
+                />
+              </li>
+            ))}
+          </DropdownItemContainer>
+        )}
+      </DropdownContainer>
+    </div>
+  );
+};
+
+export default DropdownColorLegend;
diff --git a/libs/shared/lib/components/colorComponents/colorLegend/colorLegendSeqDiv.stories.tsx b/libs/shared/lib/components/colorComponents/colorLegend/colorLegendSeqDiv.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..57e8ae4501effe678b20d6473e3df6ad82e78eb2
--- /dev/null
+++ b/libs/shared/lib/components/colorComponents/colorLegend/colorLegendSeqDiv.stories.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { Meta } from '@storybook/react';
+import ColorLegend from '.';
+
+export default {
+  title: 'ColorManager/Legend/ColorLegend',
+  component: ColorLegend,
+  decorators: [(story) => <div style={{ margin: '0', width: '300px', height: '50px' }}>{story()}</div>],
+} as Meta<typeof ColorLegend>;
+
+export const Default = {
+  args: {
+    colors: {
+      5: 'hsl(220 80% 98%)',
+      10: 'hsl(220 71% 96%)',
+      20: 'hsl(220 95% 92%)',
+      30: 'hsl(220 92% 85%)',
+      40: 'hsl(220 94% 75%)',
+      50: 'hsl(220 92% 67%)',
+      60: 'hsl(220 84% 58%)',
+      70: 'hsl(220 79% 49%)',
+      80: 'hsl(220 86% 36%)',
+      90: 'hsl(220 80% 23%)',
+      95: 'hsl(220 84% 17%)',
+      100: 'hsl(220 61% 13%)',
+    },
+
+    //colors: dataColors.blue,
+    showAxis: false,
+    data: { min: 0, max: 100 },
+    tickCount: 5,
+    name: 'seq:blue',
+  },
+};
diff --git a/libs/shared/lib/components/colorComponents/colorLegend/index.tsx b/libs/shared/lib/components/colorComponents/colorLegend/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..bd2e3154e258f4059a722e749fe597622a99e9eb
--- /dev/null
+++ b/libs/shared/lib/components/colorComponents/colorLegend/index.tsx
@@ -0,0 +1,93 @@
+import React, { useEffect, useRef } from 'react';
+import * as d3 from 'd3';
+
+export type ColorLegendProps = {
+  colors: { [key: number]: string };
+  //colors: string;
+  data: { min: number; max: number };
+  name: string;
+  showAxis: boolean;
+  tickCount?: number; // Optional prop for specifying tick count
+};
+
+export const ColorLegend = ({ colors, data, tickCount = 5, name, showAxis }: ColorLegendProps) => {
+  const svgRef = useRef<SVGSVGElement | null>(null);
+  useEffect(() => {
+    if (!svgRef.current) return;
+
+    const widthSVG: number = +svgRef.current.clientWidth;
+    const heightSVG: number = +svgRef.current.clientHeight;
+
+    // Parse HSL strings
+
+    const hslValues = Object.values(colors)
+      .map((hslString) => {
+        return hslString;
+      })
+      .reverse();
+
+    // Set up SVG container
+    const svg = d3.select(svgRef.current);
+    svg.selectAll('*').remove(); // Clear previous content
+
+    //const marginPercentage = { top: 0.15, right: 0.1, bottom: 0.15, left: 0.1 };
+    const marginPercentage = { top: 0.0, right: 0.0, bottom: 0.0, left: 0.0 };
+    const margin = {
+      top: marginPercentage.top * heightSVG,
+      right: marginPercentage.right * widthSVG,
+      bottom: marginPercentage.bottom * heightSVG,
+      left: marginPercentage.left * widthSVG,
+    };
+
+    const groupMargin = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
+    const widthSVGwithinMargin: number = widthSVG - margin.left - margin.right;
+    const heightSVGwithinMargin: number = heightSVG - margin.top - margin.bottom;
+
+    const idGradient: string = `clrGradient_${name}`;
+    // Create a gradient
+    let gradient = groupMargin
+      .append('defs')
+      .append('linearGradient')
+      .attr('id', idGradient)
+      .attr('x1', '0%')
+      .attr('y1', '0%')
+      .attr('x2', '100%')
+      .attr('y2', '0%');
+
+    // Add color stops to the gradient
+    gradient.selectAll('stop').remove();
+
+    for (let i = 0; i < hslValues.length; i++) {
+      gradient
+        .append('stop')
+        .attr('offset', `${(i / (hslValues.length - 1)) * 100}%`)
+        .style('stop-color', hslValues[i] as string);
+    }
+
+    groupMargin
+      .append('rect')
+      .attr('width', widthSVGwithinMargin)
+      .attr('height', heightSVGwithinMargin)
+      .style('stroke', 'gray')
+      .style('fill', `url(#${idGradient})`);
+    //.style('fill', colors);
+
+    if (showAxis) {
+      const xScale = d3.scaleLinear().domain([data.min, data.max]).range([0, widthSVGwithinMargin]);
+
+      const xAxis = d3.axisBottom(xScale).ticks(tickCount);
+
+      groupMargin.append('g').attr('class', 'x-axis').attr('transform', `translate(0, ${heightSVGwithinMargin})`).call(xAxis);
+
+      svg.selectAll('.tick text').attr('class', 'font-sans text-primary font-semibold').style('stroke', 'none');
+    }
+  }, [colors, data, tickCount]);
+
+  return (
+    <div className="w-full h-full">
+      <svg ref={svgRef} className="container" width="100%" height="100%"></svg>
+    </div>
+  );
+};
+
+export default ColorLegend;
diff --git a/libs/shared/lib/components/forms/index.tsx b/libs/shared/lib/components/forms/index.tsx
index 1947721508774733c813cc8df95b53e40a54a76c..643126cacb855854cca41d87e5e90769dfd681ec 100644
--- a/libs/shared/lib/components/forms/index.tsx
+++ b/libs/shared/lib/components/forms/index.tsx
@@ -75,13 +75,13 @@ export const FormBody = ({
   children,
   ...props
 }: PropsWithChildren<React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>>) => (
-  <form className="card-body px-0 w-72 py-5" {...props}>
+  <form className="card-body px-0 w-72 py-0" {...props}>
     {children}
   </form>
 );
 export const FormTitle = ({ children, title, onClose }: PropsWithChildren<{ title: string; onClose: () => void }>) => {
   return (
-    <div className="card-title p-5 py-0 flex w-full">
+    <div className="card-title p-5 py-0 mt-2 flex w-full">
       <h2 className="w-full">{title}</h2>
       <Button rounded variant="ghost" iconComponent={<Close />} onClick={() => onClose()} />
     </div>
@@ -90,7 +90,7 @@ export const FormTitle = ({ children, title, onClose }: PropsWithChildren<{ titl
 export const FormHBar = () => <div className="divider m-0"></div>;
 export const FormControl = ({ children }: PropsWithChildren) => <div className="form-control px-5">{children}</div>;
 export const FormActions = (props: { onClose: () => void }) => (
-  <div className="grid grid-cols-2 px-5 gap-2">
+  <div className="grid grid-cols-2 px-5 gap-2 mb-2">
     <Button
       type="secondary"
       variant="outline"
diff --git a/libs/shared/lib/components/icon/index.tsx b/libs/shared/lib/components/icon/index.tsx
index 937427828655e25f3c7ac116e0bd272765f9c13f..68950afb578f4358dc6ea48dcaf443bd4f7ef4b9 100644
--- a/libs/shared/lib/components/icon/index.tsx
+++ b/libs/shared/lib/components/icon/index.tsx
@@ -1,7 +1,7 @@
 import React, { ReactElement } from 'react';
 import { SVGProps } from 'react';
 
-export type Sizes = 16 | 20 | 24 | 28 | 32 | 40;
+export type Sizes = 14 | 16 | 20 | 24 | 28 | 32 | 40;
 export type IconProps = SVGProps<SVGSVGElement> & {
   component: ReactElement<any>;
   size?: Sizes;
diff --git a/libs/shared/lib/components/info/index.tsx b/libs/shared/lib/components/info/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a9a5f485e0774f40d21215699f9ee6ffeea18571
--- /dev/null
+++ b/libs/shared/lib/components/info/index.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import Icon from '../icon';
+import Tooltip from '../tooltip';
+import { InfoOutlined } from '@mui/icons-material';
+
+type Props = {
+  tooltip: string;
+};
+
+export default function Info({ tooltip }: Props) {
+  return (
+    <Tooltip position="top" tooltip={tooltip}>
+      <Icon component={<InfoOutlined />} size={14} />
+    </Tooltip>
+  );
+}
diff --git a/libs/shared/lib/components/info/info.stories.tsx b/libs/shared/lib/components/info/info.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3104401ffd8743a675cc54029838d3dba402b068
--- /dev/null
+++ b/libs/shared/lib/components/info/info.stories.tsx
@@ -0,0 +1,27 @@
+import { StoryObj, Meta } from '@storybook/react';
+import Info from '.';
+
+export default {
+  title: 'Components/Info',
+  component: Info,
+  decorators: [(Story) => <div className="flex m-5">{Story()}</div>],
+  argTypes: {
+    name: {
+      control: 'select',
+      options: ['ArrowBack', 'DeleteOutline', 'KeyboardArrowLeft', 'Settings'],
+    },
+    size: {
+      control: 'radio',
+      options: [16, 20, 24, 28, 32, 40],
+    },
+  },
+} as Meta;
+type Story = StoryObj<typeof Info>;
+
+const InfoButton: Story = {
+  args: {
+    tooltip: 'ArrowBack',
+  },
+};
+
+export const Default = { ...InfoButton };
diff --git a/libs/shared/lib/components/inputs/index.tsx b/libs/shared/lib/components/inputs/index.tsx
index 2595b8038c45ed3f2dc3f4f10611e8df61365ecd..b06e1cdbb46672fdea7a29cdecca7fb5ed71cf95 100644
--- a/libs/shared/lib/components/inputs/index.tsx
+++ b/libs/shared/lib/components/inputs/index.tsx
@@ -58,12 +58,13 @@ type RadioProps = {
 
 type DropdownProps = {
   label?: string;
-  value: string | number;
+  value: string | number | undefined;
   type: 'dropdown';
   options: any;
   tooltip?: string;
   onChange?: (value: string | number) => void;
   required?: boolean;
+  disabled?: boolean;
 };
 
 export type InputProps = TextProps | SliderProps | CheckboxProps | DropdownProps | RadioProps | BooleanProps;
@@ -236,7 +237,7 @@ export const BooleanInput = ({ label, value, onChange, tooltip }: BooleanProps)
   );
 };
 
-export const DropDownInput = ({ label, value, options, onChange, required = false, tooltip }: DropdownProps) => {
+export const DropDownInput = ({ label, value, options, onChange, required = false, tooltip, disabled = false }: DropdownProps) => {
   const dropdownRef = React.useRef<HTMLDivElement>(null);
   const [isDropdownOpen, setIsDropdownOpen] = React.useState<boolean>(false);
 
@@ -266,6 +267,7 @@ export const DropDownInput = ({ label, value, options, onChange, required = fals
       <DropdownContainer className="w-full" ref={dropdownRef}>
         <DropdownButton
           title={value}
+          disabled={disabled}
           onClick={(e) => {
             e.stopPropagation();
             e.preventDefault();
diff --git a/libs/shared/lib/components/tooltip/index.tsx b/libs/shared/lib/components/tooltip/index.tsx
index f44efea2c864bb25216ffde980d735afa2ad12e5..060cb73eac7a09426f67e675af65da3ff71b78a7 100644
--- a/libs/shared/lib/components/tooltip/index.tsx
+++ b/libs/shared/lib/components/tooltip/index.tsx
@@ -1,12 +1,12 @@
-import React from 'react';
+import React, { ReactNode } from 'react';
 
-export interface TooltipProps {
+export interface BarTooltipProps {
   x: number;
   y: number;
   content: React.ReactNode;
 }
 
-const Tooltip: React.FC<TooltipProps> = ({ x, y, content }) => {
+export const BarplotTooltip: React.FC<BarTooltipProps> = ({ x, y, content }) => {
   return (
     <div className="absolute font-sans bg-light border border-secondary text-secondary p-2" style={{ left: `${x}px`, top: `${y}px` }}>
       {content}
@@ -14,4 +14,44 @@ const Tooltip: React.FC<TooltipProps> = ({ x, y, content }) => {
   );
 };
 
-export default Tooltip;
+export type TooltipProps = {
+  position?: 'top' | 'bottom' | 'left' | 'right';
+  children: ReactNode;
+  tooltip: string;
+};
+
+export default function Tooltip({ position = 'bottom', children, tooltip }: TooltipProps) {
+  return (
+    <div className="group relative cursor-pointer">
+      <div className="mx-2 my-2">{children}</div>
+      <span
+        className={`absolute hidden group-hover:inline-block bg-neutral-900 text-white text-xs p-2 whitespace-nowrap rounded ${
+          position === 'top' ? 'left-1/2 -translate-x-1/2 bottom-[calc(100%+5px)]' : ''
+        } ${position === 'bottom' ? 'left-1/2 -translate-x-1/2 top-[calc(100%+5px)]' : ''} ${
+          position === 'left' ? 'top-1/2 -translate-y-1/2 right-[calc(100%+5px)]' : ''
+        } ${position === 'right' ? 'top-1/2 -translate-y-1/2 left-[calc(100%+5px)]' : ''} `}
+      >
+        {tooltip}
+      </span>
+      <span
+        className={`absolute hidden group-hover:inline-block border-[6px] ${
+          position === 'top'
+            ? 'left-1/2 -translate-x-1/2 bottom-full border-l-transparent border-r-transparent border-b-0 border-t-neutral-900'
+            : ''
+        } ${
+          position === 'bottom'
+            ? 'left-1/2 -translate-x-1/2 top-full border-l-transparent border-r-transparent border-t-0 border-b-neutral-900'
+            : ''
+        } ${
+          position === 'left'
+            ? 'top-1/2 -translate-y-1/2 right-full border-t-transparent border-b-transparent border-r-0 border-l-neutral-900'
+            : ''
+        } ${
+          position === 'right'
+            ? 'top-1/2 -translate-y-1/2 left-full border-t-transparent border-b-transparent border-l-0 border-r-neutral-900'
+            : ''
+        } `}
+      ></span>
+    </div>
+  );
+}
diff --git a/libs/shared/lib/components/tooltip/tooltip.stories.tsx b/libs/shared/lib/components/tooltip/tooltip.stories.tsx
index 4a3ee29ab79efdf53b96691da4c318d87e3a4a12..1004f713a26c44dff67e895546454a5741c9ee20 100644
--- a/libs/shared/lib/components/tooltip/tooltip.stories.tsx
+++ b/libs/shared/lib/components/tooltip/tooltip.stories.tsx
@@ -7,22 +7,62 @@ export default {
   component: Tooltip,
 } as Meta<typeof Tooltip>;
 
-export const BarplotTooltip = (args: TooltipProps) => {
-  const tooltipContent = (
-    <div>
-      <span>{'VVD'}</span>: <span>{30}</span>
+export const TooltipBottom = (args: TooltipProps) => {
+  return (
+    <div className="flex m-5">
+      <Tooltip {...args}>
+        <h1>Hover over me</h1>
+      </Tooltip>
+    </div>
+  );
+};
+
+TooltipBottom.args = {
+  position: 'bottom',
+  tooltip: 'This is a tooltip',
+};
+
+export const TooltipTop = (args: TooltipProps) => {
+  return (
+    <div className="flex m-5">
+      <Tooltip {...args}>
+        <h1>Hover over me</h1>
+      </Tooltip>
+    </div>
+  );
+};
+
+TooltipTop.args = {
+  position: 'top',
+  tooltip: 'This is a tooltip',
+};
+
+export const TooltipLeft = (args: TooltipProps) => {
+  return (
+    <div className="flex m-5">
+      <Tooltip {...args}>
+        <h1>Hover over me</h1>
+      </Tooltip>
     </div>
   );
+};
+
+TooltipLeft.args = {
+  position: 'left',
+  tooltip: 'This is a tooltip',
+};
 
+export const TooltipRight = (args: TooltipProps) => {
   return (
-    <div style={{ position: 'relative' }}>
-      <Tooltip {...args} content={tooltipContent} />
+    <div className="flex m-5">
+      <Tooltip {...args}>
+        <h1>Hover over me</h1>
+      </Tooltip>
     </div>
   );
 };
 
-BarplotTooltip.args = {
-  x: 100,
-  y: 100,
-  content: 'This is a tooltip',
+TooltipRight.args = {
+  position: 'right',
+  tooltip: 'This is a tooltip',
 };
diff --git a/libs/shared/lib/data-access/api/eventBus.tsx b/libs/shared/lib/data-access/api/eventBus.tsx
index 0c7038aa27ac0dae6c0c48e25dc3027e236338cf..0cb409ceb95e017580c074c727227361d3488567 100644
--- a/libs/shared/lib/data-access/api/eventBus.tsx
+++ b/libs/shared/lib/data-access/api/eventBus.tsx
@@ -3,11 +3,8 @@ import {
   useAuthorizationCache,
   useAppDispatch,
   useSessionCache,
-  useQuerybuilderGraph,
   useQuerybuilderHash,
-  useML,
   useMLEnabledHash,
-  useConfig,
   useQuerybuilderSettings,
   readInSchemaFromBackend,
   assignNewGraphQueryResult,
@@ -15,10 +12,15 @@ import {
   useVisualizationState,
   wsSchemaRequest,
   wsSchemaSubscription,
+  useQuerybuilderGraph,
+  useGraphQueryResult,
 } from '@graphpolaris/shared/lib/data-access';
 import { Broker, wsQuerySubscription, wsQueryTranslationSubscription } from '@graphpolaris/shared/lib/data-access/broker';
 import { addInfo } from '@graphpolaris/shared/lib/data-access/store/configSlice';
-import { GraphQueryResultFromBackendPayload, queryingBackend } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
+import {
+  GraphQueryResultFromBackendPayload,
+  addGraphQueryStatistics,
+} from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
 import { allMLTypes, LinkPredictionInstance, setMLResult } from '@graphpolaris/shared/lib/data-access/store/mlSlice';
 import { QueryBuilderText, setQueryText, setQuerybuilderNodes } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
 import { SchemaFromBackend } from '@graphpolaris/shared/lib/schema';
@@ -47,6 +49,8 @@ import {
 import { URLParams, getParam, deleteParam } from './url';
 import { setVisualizationState } from '../store/visualizationSlice';
 import { isEqual } from 'lodash-es';
+import { addSchemaAttributeDimensions } from '../store/schemaSlice';
+import { QueryMultiGraph } from '../../querybuilder';
 
 export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }) => {
   const { login } = useAuth();
@@ -57,7 +61,9 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
   const queryBuilder = useQuerybuilder();
   const mlHash = useMLEnabledHash();
   const visState = useVisualizationState();
+  const query = useQuerybuilderGraph() as QueryMultiGraph;
   const queryBuilderSettings = useQuerybuilderSettings();
+  const graphQueryResult = useGraphQueryResult();
 
   function loadSaveState(saveStateID: string | undefined, saveStates: Record<string, SaveStateI>) {
     if (saveStateID && saveStates && saveStateID in saveStates) {
@@ -70,6 +76,12 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
     }
   }
 
+  useEffect(() => {
+    if (graphQueryResult.nodes.length > 0 || graphQueryResult.edges.length > 0) {
+      dispatch(addGraphQueryStatistics(query));
+    }
+  }, [graphQueryResult.nodes, graphQueryResult.edges]);
+
   useEffect(() => {
     const unsubs: Function[] = [];
 
@@ -93,6 +105,11 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
       }),
     );
 
+    Broker.instance().subscribe((data: any) => {
+      dispatch(addSchemaAttributeDimensions(data));
+      dispatch(addInfo('Attribute dimensions added'));
+    }, 'schema_inference');
+
     // Broker.instance().subscribe((data: QueryBuilderState) => dispatch(setQuerybuilderNodes(data)), 'query_builder_state');
 
     allMLTypes.forEach((mlType) => {
@@ -137,6 +154,12 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
       }),
     );
 
+    Broker.instance().subscribe((response: QueryBuilderText) => {
+      if (response && response.result) {
+        dispatch(setQueryText(response));
+      }
+    }, 'query_translation_result');
+
     login();
 
     // Setup cleanup
@@ -145,6 +168,9 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
       unsubs.forEach((unsub) => {
         unsub();
       });
+            Broker.instance().unSubscribeAll('query_translation_result');
+      Broker.instance().unSubscribeAll('schema_inference');
+
     };
   }, []);
 
diff --git a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
index 4c3668126d1275ee185095b77559577311901680..557afdaf365b55c6741eb79441062493944bc7cc 100755
--- a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
+++ b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
@@ -1,5 +1,7 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 import type { RootState } from './store';
+import { GraphStatistics, retrieveGraphStatistics } from '../utils/result-statistics';
+import { QueryMultiGraph } from '../../querybuilder';
 
 export interface GraphQueryResultFromBackendPayload {
   queryID: string;
@@ -54,6 +56,7 @@ export interface GraphQueryResult {
   // Describes what entities there are in this graph query result.
   nodeTypes: string[];
   queryingBackend: boolean;
+  statistics?: GraphStatistics;
 }
 
 // Define the initial state using that type
@@ -118,14 +121,21 @@ export const graphQueryResultSlice = createSlice({
     queryingBackend: (state) => {
       state.queryingBackend = true;
     },
+    addGraphQueryStatistics: (state, action: PayloadAction<QueryMultiGraph>) => {
+      if (state.nodes.length > 0 && state.edges.length > 0) {
+        state.statistics = retrieveGraphStatistics({ nodes: state.nodes, edges: state.edges }, action.payload);
+      }
+    },
   },
 });
 
-export const { assignNewGraphQueryResult, resetGraphQueryResults, queryingBackend } = graphQueryResultSlice.actions;
+export const { assignNewGraphQueryResult, resetGraphQueryResults, queryingBackend, addGraphQueryStatistics } =
+  graphQueryResultSlice.actions;
 
 // Other code such as selectors can use the imported `RootState` type
 export const selectGraphQueryResult = (state: RootState) => state.graphQueryResult;
 export const selectGraphQueryResultNodes = (state: RootState) => state.graphQueryResult.nodes;
 export const selectGraphQueryResultLinks = (state: RootState) => state.graphQueryResult.edges;
+export const selectGraphQueryResultStats = (state: RootState) => state.graphQueryResult.statistics || {};
 
 export default graphQueryResultSlice.reducer;
diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts
index 282db06af7fc37f251898ff8cb92c820b1b23668..920170edfef41334277ff9e3532d21de13894053 100644
--- a/libs/shared/lib/data-access/store/hooks.ts
+++ b/libs/shared/lib/data-access/store/hooks.ts
@@ -1,5 +1,5 @@
 import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
-import { selectGraphQueryResult } from './graphQueryResultSlice';
+import { selectGraphQueryResult, selectGraphQueryResultStats } from './graphQueryResultSlice';
 import { schemaGraph, selectSchemaLayout, schemaSettingsState } from './schemaSlice';
 import type { RootState, AppDispatch } from './store';
 import { configState } from '@graphpolaris/shared/lib/data-access/store/configSlice';
@@ -11,7 +11,14 @@ import {
 } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
 import { sessionCacheState } from './sessionSlice';
 import { authState } from './authSlice';
-import { visualizationState, visualizationSettings } from './visualizationSlice';
+import {
+  visualizationState,
+  visualizationSettings,
+  visSettingsState,
+  visGeneralSettings,
+  visEncodingsState,
+  visInteractionsState,
+} from './visualizationSlice';
 import { allMLEnabled, selectML } from './mlSlice';
 import { searchResultState, searchResultData, searchResultSchema, searchResultQB, recentSearches } from './searchResultSlice';
 
@@ -22,6 +29,7 @@ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
 
 /** Gives the graphQueryResult from the store */
 export const useGraphQueryResult = () => useAppSelector(selectGraphQueryResult);
+export const useGraphQueryResultStats = () => useAppSelector(selectGraphQueryResultStats);
 
 // Gives the schema
 export const useSchemaGraph = () => useAppSelector(schemaGraph);
@@ -53,3 +61,7 @@ export const useRecentSearches = () => useAppSelector(recentSearches);
 // Visualization Slices
 export const useVisualizationState = () => useAppSelector(visualizationState);
 export const useVisualizationSettings = () => useAppSelector(visualizationSettings);
+export const useGeneralSettings = () => useAppSelector(visGeneralSettings);
+export const useVisSettings = () => useAppSelector(visSettingsState);
+export const useVisEncodings = () => useAppSelector(visEncodingsState);
+export const useVisInteractions = () => useAppSelector(visInteractionsState);
diff --git a/libs/shared/lib/data-access/store/schemaSlice.ts b/libs/shared/lib/data-access/store/schemaSlice.ts
index 85097c6d0adc2b179baa21a71dabd011b3acb5da..3f696c4aea2940806ee8208e1d2c4388d2fabb45 100644
--- a/libs/shared/lib/data-access/store/schemaSlice.ts
+++ b/libs/shared/lib/data-access/store/schemaSlice.ts
@@ -46,9 +46,13 @@ export const schemaSlice = createSlice({
     setSchemaSettings: (state, action: PayloadAction<SchemaSettings>) => {
       state.settings = action.payload;
     },
+
+    addSchemaAttributeDimensions: (state, action: PayloadAction<any>) => {
+      state.graph = SchemaUtils.addAttributeDimensionsToGraph(state.graph, JSON.parse(action.payload));
+    },
   },
 });
-export const { readInSchemaFromBackend, setSchema, setSchemaSettings, clearSchema } = schemaSlice.actions;
+export const { readInSchemaFromBackend, setSchema, setSchemaSettings, clearSchema, addSchemaAttributeDimensions } = schemaSlice.actions;
 
 export const schemaSettingsState = (state: RootState) => state.schema.settings;
 
diff --git a/libs/shared/lib/data-access/store/visualizationSlice.ts b/libs/shared/lib/data-access/store/visualizationSlice.ts
index c5b1d281c4207ab828f8b45bb885db3327853f1c..c2a5ce70b49ed194ce0b3be3424b4704c4b51e60 100644
--- a/libs/shared/lib/data-access/store/visualizationSlice.ts
+++ b/libs/shared/lib/data-access/store/visualizationSlice.ts
@@ -1,35 +1,24 @@
 import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
 import type { RootState } from './store';
-import { globalConfigSchemaTypes, localConfigSchemaType } from '../../vis/types';
+import { globalConfigTypes, VisualizationConfiguration } from '../../vis/types';
 import { isEqual } from 'lodash-es';
-
-export enum Visualizations {
-  NodeLink = 'NodeLinkVis',
-  Paohvis = 'PaohVis',
-  RawJSON = 'RawJSONVis',
-  Table = 'TableVis',
-  Matrix = 'MatrixVis',
-}
+import { EncodingTypes } from '../../vis/configuration/encodings';
+import { SettingTypes } from '../../vis/configuration/settings';
+import { InteractionTypes } from '../../vis/configuration/interactions';
 
 export type VisState = {
-  activeVisualization: Visualizations;
+  activeVisualization: string;
   settings: {
-    general: globalConfigSchemaTypes;
-    [id: string]: localConfigSchemaType;
+    general: globalConfigTypes;
+  } & {
+    [id: string]: VisualizationConfiguration;
   };
 };
 
 export const initialState: VisState = {
-  activeVisualization: Visualizations.NodeLink,
+  activeVisualization: 'NodeLink',
   settings: {
-    general: {
-      // Example general setting
-      showLegend: {
-        value: false,
-        label: 'Show legend',
-        type: 'boolean',
-      },
-    },
+    general: {},
   },
 };
 
@@ -37,34 +26,60 @@ export const visualizationSlice = createSlice({
   name: 'visualization',
   initialState,
   reducers: {
-    addVisualization: (state, action: PayloadAction<{ id: string; settings: localConfigSchemaType }>) => {
-      const { id, settings } = action.payload;
-      state.settings[id] = settings;
+    addVisualization: (
+      state,
+      action: PayloadAction<{
+        id: string;
+        settings: SettingTypes;
+        encodings: EncodingTypes;
+        interactions: InteractionTypes;
+      }>,
+    ) => {
+      const { id, settings, encodings, interactions } = action.payload;
+      state.settings[id] = { settings, encodings, interactions };
     },
     removeVisualization: (state, action: PayloadAction<string>) => {
       if (state.settings[action.payload]) {
         delete state.settings[action.payload];
       }
     },
-    updateVisualizationSettings: (state, action: PayloadAction<{ id: string; settings: any }>) => {
-      const { id, settings } = action.payload;
-      state.settings[id] = { ...state.settings[id], ...settings };
+    updateVisualizationSettings: (state, action: PayloadAction<{ id: string; configuration: any }>) => {
+      const { id, configuration } = action.payload;
+      state.settings[id] = { ...state.settings[id], ...configuration };
     },
-    updateGeneralSettings: (state, action: PayloadAction<any>) => {
+    updateGeneralSettings: (state, action: PayloadAction<globalConfigTypes>) => {
       state.settings.general = action.payload;
     },
-    setActiveVisualization: (state, action: PayloadAction<Visualizations>) => {
+    setActiveVisualization: (state, action: PayloadAction<string>) => {
       state.activeVisualization = action.payload;
     },
-    updateSettings: (state, action: PayloadAction<any>) => {
-      state.settings.visualization = action.payload;
-    },
     setVisualizationState: (state, action: PayloadAction<VisState>) => {
       if (action.payload.activeVisualization && !isEqual(action.payload, state)) {
         state.activeVisualization = action.payload.activeVisualization;
         state.settings = action.payload.settings;
       }
     },
+    updateGeneralConfiguration: (state, action: PayloadAction<any>) => {
+      state.settings.general = action.payload;
+    },
+    updateVisConfiguration: (state, action: PayloadAction<any>) => {
+      state.settings[state.activeVisualization] = action.payload;
+    },
+    updateVisSettings: (state, action: PayloadAction<any>) => {
+      if (state.activeVisualization && state.settings[state.activeVisualization]) {
+        state.settings[state.activeVisualization]!.settings = action.payload;
+      }
+    },
+    updateVisEncodings: (state, action: PayloadAction<any>) => {
+      if (state.activeVisualization && state.settings[state.activeVisualization]) {
+        state.settings[state.activeVisualization]!.encodings = action.payload;
+      }
+    },
+    updateVisInteractions: (state, action: PayloadAction<any>) => {
+      if (state.activeVisualization && state.settings[state.activeVisualization]) {
+        state.settings[state.activeVisualization]!.interactions = action.payload;
+      }
+    },
   },
 });
 
@@ -74,15 +89,24 @@ export const {
   updateVisualizationSettings,
   updateGeneralSettings,
   setActiveVisualization,
-  updateSettings,
   setVisualizationState,
+  updateGeneralConfiguration,
+  updateVisConfiguration,
+  updateVisSettings,
+  updateVisEncodings,
+  updateVisInteractions,
 } = visualizationSlice.actions;
 
 export const visualizationState = (state: RootState) => state.visualize;
 export const visualizationAllSettings = (state: RootState) => state.visualize.settings;
 export const visualizationSettings = createSelector([visualizationAllSettings, visualizationState], (settings, visualize) => ({
   general: settings.general,
-  [visualize.activeVisualization]: settings[visualize.activeVisualization],
+  [visualize.activeVisualization]: visualize.settings[visualize.activeVisualization],
 }));
 
+export const visGeneralSettings = (state: RootState) => state.visualize.settings.general ?? {};
+export const visSettingsState = (state: RootState) => state.visualize.settings[state.visualize.activeVisualization]?.settings ?? {};
+export const visEncodingsState = (state: RootState) => state.visualize.settings[state.visualize.activeVisualization]?.encodings ?? {};
+export const visInteractionsState = (state: RootState) => state.visualize.settings[state.visualize.activeVisualization]?.interactions ?? {};
+
 export default visualizationSlice.reducer;
diff --git a/libs/shared/lib/data-access/utils/result-statistics/compress-graph.ts b/libs/shared/lib/data-access/utils/result-statistics/compress-graph.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5b12b06b3e802f728d72dcdee37be50568e54d1a
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/compress-graph.ts
@@ -0,0 +1,32 @@
+import { Edge, Node } from '../../store';
+import { CompressedElement, CompressedGraph } from './result-statistics.types';
+
+const compressElement = (element: Node[] | Edge[]) => {
+  const compressed: CompressedElement = {};
+
+  element.forEach((item) => {
+    const { label, attributes } = item;
+    if (!compressed[label]) {
+      compressed[label] = {};
+    }
+    Object.entries(attributes).forEach(([attributeId, attributeValue]) => {
+      if (!compressed[label][attributeId]) {
+        compressed[label][attributeId] = [];
+      }
+      compressed[label][attributeId].push(attributeValue);
+    });
+  });
+
+  return compressed;
+};
+
+export const compressGraph = (graph: { nodes: Node[]; edges: Edge[] }): CompressedGraph => {
+  try {
+    return {
+      nodes: compressElement(graph.nodes),
+      edges: compressElement(graph.edges),
+    };
+  } catch (error) {
+    return { nodes: {}, edges: {} };
+  }
+};
diff --git a/libs/shared/lib/data-access/utils/result-statistics/dimensions.ts b/libs/shared/lib/data-access/utils/result-statistics/dimensions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..75393911106ab1f10d1ff41f1c4f1efc1b991e72
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/dimensions.ts
@@ -0,0 +1,39 @@
+import { CategoricalStats, NumericalStats, SpatialStats, TemporalStats } from './result-statistics.types';
+
+export function extractCategoricalStats(data: any[]): CategoricalStats {
+  const uniqueItems = new Set(data.map((item) => JSON.stringify(item)));
+  return {
+    uniqueItems: uniqueItems.size,
+  };
+}
+
+export function extractNumericalStats(data: number[]): NumericalStats {
+  const minValue = Math.min(...data);
+  const maxValue = Math.max(...data);
+  const sum = data.reduce((acc, curr) => acc + curr, 0);
+  const avgValue = sum / data.length;
+  return {
+    minValue,
+    maxValue,
+    avgValue,
+  };
+}
+
+export function extractTemporalStats(data: Date[]): TemporalStats {
+  const sortedDates = data.slice().sort((a, b) => a.getTime() - b.getTime());
+  const minDate = sortedDates[0];
+  const maxDate = sortedDates[sortedDates.length - 1];
+  return {
+    minDate,
+    maxDate,
+  };
+}
+
+export function extractSpatialStats(data: [number, number][]): SpatialStats {
+  const totalPoints = data.length;
+  const sumCoordinates = data.reduce(([x1, y1], [x2, y2]) => [x1 + x2, y1 + y2], [0, 0]);
+  const middlePoint: [number, number] = [sumCoordinates[0] / totalPoints, sumCoordinates[1] / totalPoints];
+  return {
+    middlePoint,
+  };
+}
diff --git a/libs/shared/lib/data-access/utils/result-statistics/extract-dimensions.ts b/libs/shared/lib/data-access/utils/result-statistics/extract-dimensions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e878986494e3f38537ad680c3a70d2708dbc7608
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/extract-dimensions.ts
@@ -0,0 +1,17 @@
+import { QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder';
+import { DimensionType } from '@graphpolaris/shared/lib/schema';
+
+export const extractAttributeDimensions = (qbGraph: QueryMultiGraph, elementType: 'entity' | 'relation') => {
+  return qbGraph.nodes
+    .filter((item) => item.attributes?.type === elementType)
+    .reduce((acc: { [key: string]: { [key: string]: DimensionType } }, item) => {
+      item.attributes?.attributes?.forEach((attribute) => {
+        if (attribute.handleData && attribute.handleData.attributeName && attribute.handleData.attributeDimension) {
+          const name: string = item.attributes?.name as string;
+          acc[name] = acc[name] || {};
+          acc[name][attribute.handleData.attributeName] = attribute.handleData.attributeDimension;
+        }
+      });
+      return acc;
+    }, {});
+};
diff --git a/libs/shared/lib/data-access/utils/result-statistics/extract-stats.ts b/libs/shared/lib/data-access/utils/result-statistics/extract-stats.ts
new file mode 100644
index 0000000000000000000000000000000000000000..10c8e26e6f66b5f6b726e639f159ad44c2d63de3
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/extract-stats.ts
@@ -0,0 +1,55 @@
+import { extractCategoricalStats, extractNumericalStats, extractSpatialStats, extractTemporalStats } from './dimensions';
+import { CompressedElement, DimensionInfoType, DimensionStatistics, ElementStatisticsType } from './result-statistics.types';
+
+export const extractStatistics = (elements: CompressedElement, dimensions: DimensionInfoType) => {
+  const statistics: ElementStatisticsType = {};
+
+  Object.keys(elements).forEach((label) => {
+    statistics[label] = {};
+    Object.entries(elements[label]).forEach(([attributeId, attributeValues]) => {
+      const dimensionType = dimensions[label][attributeId];
+      statistics[label][attributeId] = { dimension: dimensionType };
+      statistics[label][attributeId].stats = calculateStatistics(attributeValues, dimensionType, statistics[label][attributeId].stats);
+    });
+  });
+
+  return statistics;
+};
+
+export const calculateStatistics = (item: any[], dimensionType: string, statsField: any) => {
+  let stats: DimensionStatistics | undefined;
+  switch (dimensionType) {
+    case 'categorical':
+      stats = extractCategoricalStats(item);
+      break;
+    case 'numerical':
+      stats = extractNumericalStats(item);
+      break;
+    case 'spatial':
+      stats = extractSpatialStats(item);
+      break;
+    case 'temporal':
+      stats = extractTemporalStats(item);
+      break;
+    default:
+      break;
+  }
+
+  if (stats !== undefined) {
+    statsField = statsField || {};
+    for (const key in stats) {
+      if (Object.prototype.hasOwnProperty.call(stats, key)) {
+        const statsKey = key as keyof DimensionStatistics;
+        if (typeof stats[statsKey] === 'number') {
+          statsField[statsKey] = (statsField[statsKey] || 0) + stats[statsKey];
+        } else if (Array.isArray(stats[statsKey])) {
+          statsField[statsKey] = (statsField[statsKey] || []).concat(stats[statsKey] as any[]);
+        } else {
+          throw new Error('Invalid stats type');
+        }
+      }
+    }
+  }
+
+  return statsField || {};
+};
diff --git a/libs/shared/lib/data-access/utils/result-statistics/graph-statistics.ts b/libs/shared/lib/data-access/utils/result-statistics/graph-statistics.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e29bfec3a37359496d5b54b5dcab26f9d4580fcc
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/graph-statistics.ts
@@ -0,0 +1,26 @@
+import { Node, Edge } from '../../store/graphQueryResultSlice';
+import { QueryMultiGraph } from '../../../querybuilder';
+import { GraphStatistics, DimensionMap, CompressedGraph } from './result-statistics.types';
+import { compressGraph } from './compress-graph';
+import { extractStatistics } from './extract-stats';
+import { extractAttributeDimensions } from './extract-dimensions';
+
+export const retrieveGraphStatistics = (graph: { nodes: Node[]; edges: Edge[] }, qbGraph: QueryMultiGraph): GraphStatistics => {
+  try {
+    const compressedGraph: CompressedGraph = compressGraph(graph);
+
+    const nodeAttributes = extractAttributeDimensions(qbGraph, 'entity');
+    const edgeAttributes = extractAttributeDimensions(qbGraph, 'relation');
+    const dimensionMap: DimensionMap = { nodes: nodeAttributes, edges: edgeAttributes };
+
+    return {
+      nodes: extractStatistics(compressedGraph.nodes, dimensionMap.nodes),
+      edges: extractStatistics(compressedGraph.edges, dimensionMap.edges),
+    };
+  } catch (error) {
+    return {
+      nodes: {},
+      edges: {},
+    };
+  }
+};
diff --git a/libs/shared/lib/data-access/utils/result-statistics/index.ts b/libs/shared/lib/data-access/utils/result-statistics/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9d4505f3fb3953dff051114553ab1aa87889d235
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/index.ts
@@ -0,0 +1,2 @@
+export { retrieveGraphStatistics } from './graph-statistics';
+export * from './result-statistics.types';
diff --git a/libs/shared/lib/data-access/utils/result-statistics/result-statistics.types.ts b/libs/shared/lib/data-access/utils/result-statistics/result-statistics.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ad48b7823d3afd28ffbf40ba09774ce4adaf16fc
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/result-statistics.types.ts
@@ -0,0 +1,52 @@
+import { DimensionType } from '../../../schema';
+
+export type CategoricalStats = {
+  uniqueItems: number;
+};
+
+export type NumericalStats = {
+  minValue: number;
+  maxValue: number;
+  avgValue: number;
+};
+
+export type TemporalStats = {
+  minDate: Date;
+  maxDate: Date;
+};
+
+export type SpatialStats = {
+  middlePoint: [number, number];
+};
+
+export type DimensionStatistics = CategoricalStats | NumericalStats | TemporalStats | SpatialStats;
+
+export type DimensionInfoType = { [label: string]: { [attribute: string]: DimensionType } };
+
+export type DimensionMap = {
+  nodes: DimensionInfoType;
+  edges: DimensionInfoType;
+};
+
+export type ElementStats = {
+  dimension: DimensionType;
+  stats?: DimensionStatistics;
+};
+
+export type ElementStatisticsType = {
+  [label: string]: { [attribute: string]: ElementStats };
+};
+
+export interface GraphStatistics {
+  nodes: ElementStatisticsType;
+  edges: ElementStatisticsType;
+}
+
+export type CompressedElement = {
+  [label: string]: { [attribute: string]: any };
+};
+
+export type CompressedGraph = {
+  nodes: CompressedElement;
+  edges: CompressedElement;
+};
diff --git a/libs/shared/lib/querybuilder/model/graphology/model.ts b/libs/shared/lib/querybuilder/model/graphology/model.ts
index 1f851ab78eff9746c25ce86a3ce148c89e35ec5f..dc4a2bc1a42ebfa4cc9a2f823458dd3fa4a1cd42 100644
--- a/libs/shared/lib/querybuilder/model/graphology/model.ts
+++ b/libs/shared/lib/querybuilder/model/graphology/model.ts
@@ -2,7 +2,7 @@ import { Attributes as GAttributes } from 'graphology-types';
 import { XYPosition } from 'reactflow';
 import { MultiGraph } from 'graphology';
 import './utils';
-import { GeneralDescription, InputNodeType, InputNodeTypeTypes } from '../logic/general';
+import { GeneralDescription, InputNodeDimension, InputNodeType, InputNodeTypeTypes } from '../logic/general';
 import { AllLogicTypes } from '../logic';
 import { Handles, QueryElementTypes } from '../reactflow';
 
@@ -64,6 +64,7 @@ export type QueryGraphNodes = EntityNodeAttributes | RelationNodeAttributes | Lo
 export type QueryGraphEdgeAttribute = {
   attributeName?: string;
   attributeType?: InputNodeType;
+  attributeDimension?: InputNodeDimension;
 };
 
 export type QueryGraphEdgeHandle = {
diff --git a/libs/shared/lib/querybuilder/model/graphology/utils.ts b/libs/shared/lib/querybuilder/model/graphology/utils.ts
index d97b7fadaf5ce92bb5551a7f7939aafa531b4935..7c28788261f2921997d7f18c36fd572256c40c07 100644
--- a/libs/shared/lib/querybuilder/model/graphology/utils.ts
+++ b/libs/shared/lib/querybuilder/model/graphology/utils.ts
@@ -79,6 +79,7 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
             ...defaultHandleData,
             attributeName: optAttribute.name,
             attributeType: optAttribute.type,
+            attributeDimension: optAttribute.dimension,
             handleType: Handles.EntityAttribute,
           },
         });
@@ -94,6 +95,7 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
             ...defaultHandleData,
             attributeName: optAttribute.name,
             attributeType: optAttribute.type,
+            attributeDimension: optAttribute.dimension,
             handleType: Handles.RelationAttribute,
           },
         });
@@ -140,7 +142,7 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
     source: QueryGraphNodes,
     target: QueryGraphNodes,
     attributes: QueryGraphEdgesOpt = {},
-    options: AddEdge2GraphologyOptions = {}
+    options: AddEdge2GraphologyOptions = {},
   ): string | null {
     let sourceAttributeName = '';
     let sourceAttributeType: InputNodeType | undefined;
diff --git a/libs/shared/lib/querybuilder/model/logic/general.ts b/libs/shared/lib/querybuilder/model/logic/general.ts
index 9d233739336f3d4c2a9eac8ff3455d875150fc59..16e81c19e46f4566a7906832a675a4f3ac305c42 100644
--- a/libs/shared/lib/querybuilder/model/logic/general.ts
+++ b/libs/shared/lib/querybuilder/model/logic/general.ts
@@ -1,5 +1,6 @@
 export type InputNodeTypeTypes = string | number | boolean;
 export type InputNodeType = 'string' | 'float' | 'int' | 'bool' | 'date' | 'time' | 'datetime' | 'duration';
+export type InputNodeDimension = 'categorical' | 'numerical' | 'temporal' | 'spatial';
 
 export enum NumberFilterTypes {
   EQUAL = '==',
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
index 7f2cac7884c531f49508ed0787bba2ef9f83f335..d8f27749d5766c7ce569a507f0fd7d56dc3c952c 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
@@ -49,12 +49,12 @@ export const EntityFlowElement = React.memo((node: SchemaReactflowEntityNode) =>
   return (
     <div className="p-3 bg-transparent" onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
       <div className={`rounded-sm shadow min-w-[8rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#FFA952] to-[#D66700]`}>
-        <div className={`py-1 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-50'}`}>
+        <div className={`pt-1 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-50'}`}>
           <FilterHandle
             handle={data.leftRelationHandleId}
             type="target"
             position={Position.Left}
-            className={'!top-8 !left-2 !bg-accent-700 !rounded-none'}
+            className={'!top-8 !left-2 !bg-danger-700 !rounded-none'}
           />
           <FilterHandle
             handle={data.rightRelationHandleId}
diff --git a/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx b/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
index 315b9c87c3a5c99c6fac135777eed022860938ce..5e704d6d2193c35e09f1249706eebe5275f45fc9 100644
--- a/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
+++ b/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
@@ -1,10 +1,10 @@
-import { useMemo } from 'react';
+import { useMemo, ReactNode, ReactElement } from 'react';
 import { NodeAttribute, QueryGraphEdges, SchemaReactflowEntityNode, handleDataFromReactflowToDataId, toHandleId } from '../../model';
-import { sortedIndex } from 'lodash-es';
-import { Handle, Position } from 'reactflow';
+import { Position } from 'reactflow';
 import styles from './pilldropdown.module.scss';
-import { styleHandleMap } from '../utils';
 import { FilterHandle } from '../FilterHandle';
+import { Abc, CalendarToday, Map, Numbers, Place, QuestionMarkOutlined } from '@mui/icons-material';
+import Icon from '@graphpolaris/shared/lib/components/icon';
 
 type PillDropdownProps = {
   node: SchemaReactflowEntityNode;
@@ -15,6 +15,18 @@ type PillDropdownProps = {
   onHandleMouseDown: (attribute: NodeAttribute, i: number, event: React.MouseEvent) => void;
 };
 
+type IconMapType = {
+  [key: string]: ReactElement;
+};
+
+const IconMap: IconMapType = {
+  temporal: <CalendarToday />,
+  spatial: <Map />,
+  numerical: <Numbers />,
+  categorical: <Abc />,
+  default: <Place />,
+};
+
 export const PillDropdown = (props: PillDropdownProps) => {
   const forceOpen = false;
   const openNumbers: number[] = useMemo(() => {
@@ -36,7 +48,7 @@ export const PillDropdown = (props: PillDropdownProps) => {
 
   return (
     <>
-      <div className={'' + (openNumbers.length > 0 ? 'animate-openmenu ' : 'animate-closemenu ')}>
+      <div className={'divide-y divide-secondary-100' + (openNumbers.length > 0 ? 'animate-openmenu ' : 'animate-closemenu ')}>
         {openNumbers.map((i) => {
           const attribute = props.attributes[i];
           if (attribute.handleData.attributeName === undefined) {
@@ -45,23 +57,24 @@ export const PillDropdown = (props: PillDropdownProps) => {
 
           return (
             <div
-              className="px-5 py-1 font-semibold"
+              className="px-2 py-1 bg-white flex justify-between items-center"
               key={(attribute.handleData.attributeName || '') + i}
               onMouseDown={(event: React.MouseEvent) => {
                 props.onHandleMouseDown(attribute, i, event);
               }}
             >
-              {attribute.handleData.attributeName}
+              <p>{attribute.handleData.attributeName}</p>
+              {attribute.handleData?.attributeDimension && (
+                // <div className="!text-xs text-secondary-500">{IconMap[attribute.handleData.attributeDimension]}</div>
+                // <div className="!text-xs text-secondary-500">
+                <Icon component={IconMap[attribute.handleData.attributeDimension]} size={16} />
+                // </div>
+              )}
               <FilterHandle
                 handle={handleDataFromReactflowToDataId(props.node, attribute)}
                 type="source"
                 position={Position.Right}
-                className={
-                  styles.handle +
-                  ' ' +
-                  (attribute.handleData.attributeType ? styleHandleMap[attribute.handleData.attributeType] : '') +
-                  ' !top-auto !-translate-y-[145%] !right-[1.2rem]'
-                }
+                className={styles.handle + ' ' + '!top-auto mt-2 !right-[0.5rem] bg-secondary-500'}
               ></FilterHandle>
             </div>
           );
diff --git a/libs/shared/lib/schema/model/FromBackend.ts b/libs/shared/lib/schema/model/FromBackend.ts
index c080591de146b37bc78a99571c19202d6c26c10b..b5427dbf6a74dd0fc41a9e8b2c88df4e59b5c490 100644
--- a/libs/shared/lib/schema/model/FromBackend.ts
+++ b/libs/shared/lib/schema/model/FromBackend.ts
@@ -10,10 +10,13 @@ export type SchemaFromBackend = {
 
 export type SchemaAttributeTypes = 'string' | 'float' | 'int' | 'bool' | 'date' | 'time' | 'datetime' | 'duration';
 
+export type DimensionType = 'categorical' | 'numerical' | 'temporal' | 'spatial';
+
 /** Attribute type, consist of a name */
 export type SchemaAttribute = {
   name: string;
   type: SchemaAttributeTypes;
+  dimension?: DimensionType;
 };
 
 /** Node type, consist of a name and a list of attributes */
@@ -33,3 +36,8 @@ export type SchemaEdge = {
   label: string;
   type?: string;
 };
+
+export type GraphAttributeDimensions = {
+  nodes: { [id: string]: { [id: string]: string } };
+  edges: { [id: string]: { [id: string]: string } };
+};
diff --git a/libs/shared/lib/schema/schema-utils/schema-utils.ts b/libs/shared/lib/schema/schema-utils/schema-utils.ts
index 7d8dfa6992c5c666ce3cf36f966d77ddedbb0a3b..6a26bc74db8e4babbe49f09d5c6c286befbc52f8 100644
--- a/libs/shared/lib/schema/schema-utils/schema-utils.ts
+++ b/libs/shared/lib/schema/schema-utils/schema-utils.ts
@@ -1,4 +1,4 @@
-import { SchemaAttribute, SchemaFromBackend, SchemaGraphology, SchemaGraphologyNode } from '../model';
+import { DimensionType, GraphAttributeDimensions, SchemaFromBackend, SchemaGraph, SchemaGraphology, SchemaGraphologyNode } from '../model';
 
 export class SchemaUtils {
   public static schemaBackend2Graphology(schemaFromBackend: SchemaFromBackend): SchemaGraphology {
@@ -42,4 +42,34 @@ export class SchemaUtils {
     });
     return schemaGraphology;
   }
+
+  public static addAttributeDimensionsToGraph(graph: SchemaGraph, inference: GraphAttributeDimensions): SchemaGraph {
+    const { nodes, edges } = inference;
+
+    graph.nodes.forEach((node) => {
+      const dimensions = nodes[node.key];
+      if (dimensions) {
+        node.attributes?.attributes.forEach((attribute) => {
+          const dimension = dimensions[attribute.name] as DimensionType | undefined;
+          if (dimension) {
+            attribute.dimension = dimension;
+          }
+        });
+      }
+    });
+
+    graph.edges.forEach((edge) => {
+      const dimensions = edges[edge.attributes?.name];
+      if (dimensions) {
+        edge.attributes?.attributes.forEach((attribute: any) => {
+          const dimension = dimensions[attribute.name] as DimensionType | undefined;
+          if (dimension) {
+            attribute.dimension = dimension;
+          }
+        });
+      }
+    });
+
+    return graph;
+  }
 }
diff --git a/libs/shared/lib/vis/configuration/advanced/advanced.tsx b/libs/shared/lib/vis/configuration/advanced/advanced.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d7bc88177fe0dec659e75a39e13b5c825f790b21
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/advanced/advanced.tsx
@@ -0,0 +1,32 @@
+import React, { useState, useEffect } from 'react';
+import { useAppDispatch, useVisualizationState, useVisualizationSettings } from '@graphpolaris/shared/lib/data-access';
+import ReactJSONView from 'react-json-view';
+import { updateVisConfiguration } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+
+export default function AdvancedPanel() {
+  const dispatch = useAppDispatch();
+  const vis = useVisualizationState();
+  const settings = useVisualizationSettings();
+  const [updatedSettings, setUpdatedSettings] = useState(settings);
+
+  useEffect(() => {
+    if (settings !== updatedSettings) {
+      dispatch(updateVisConfiguration(updatedSettings));
+    }
+  }, [updatedSettings]);
+
+  return (
+    settings && (
+      <div className="m-2">
+        <ReactJSONView
+          src={settings[vis.activeVisualization] || {}}
+          name={false}
+          collapsed={1}
+          quotesOnKeys={false}
+          displayDataTypes={false}
+          onEdit={(v) => setUpdatedSettings(v.updated_src as any)}
+        />
+      </div>
+    )
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/advanced/index.ts b/libs/shared/lib/vis/configuration/advanced/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a7584d4b2419e2fc6b2b26fe9a4c643639719453
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/advanced/index.ts
@@ -0,0 +1 @@
+export { default as AdvancedPanel } from './advanced';
diff --git a/libs/shared/lib/vis/configuration/encodings/accessor.tsx b/libs/shared/lib/vis/configuration/encodings/accessor.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..66651f931ae0b722478d12c9d14b6680513a70ee
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/accessor.tsx
@@ -0,0 +1,62 @@
+import React, { useState, useEffect } from 'react';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { useQuerybuilderGraph } from '@graphpolaris/shared/lib/data-access';
+
+type Props = {
+  value: string | undefined;
+  onChange: (val: string | number) => void;
+  element: 'node' | 'edge';
+  dimension?: string[];
+  disabled?: boolean;
+};
+
+export default function Accessor({ value, onChange, element, dimension, disabled = false }: Props) {
+  const qbGraph = useQuerybuilderGraph();
+  const [options, setOptions] = useState<string[]>([]);
+
+  useEffect(() => {
+    let commonAttributes: Set<string> = new Set();
+
+    if (element === 'node') {
+      qbGraph.nodes
+        .filter((node) => node.attributes?.type === 'entity')
+        .forEach((node) => {
+          const nodeAttributes = new Set<string>();
+          node.attributes?.attributes?.forEach((attr) => {
+            if (attr.handleData?.attributeDimension && dimension?.includes(attr.handleData?.attributeDimension)) {
+              attr.handleData.attributeName && nodeAttributes.add(`attributes.${attr.handleData.attributeName}`);
+            }
+          });
+          if (commonAttributes.size === 0) {
+            commonAttributes = nodeAttributes;
+          } else {
+            commonAttributes = new Set([...commonAttributes].filter((x) => nodeAttributes.has(x)));
+          }
+        });
+    } else {
+      qbGraph.nodes
+        .filter((edge) => edge.attributes?.type === 'relation')
+        .forEach((edge) => {
+          const edgeAttributes = new Set<string>();
+          edge.attributes?.attributes?.forEach((attr) => {
+            if (attr.handleData?.attributeDimension && dimension?.includes(attr.handleData?.attributeDimension)) {
+              attr.handleData.attributeName && edgeAttributes.add(`attributes.${attr.handleData.attributeName}`);
+            }
+          });
+          if (commonAttributes.size === 0) {
+            commonAttributes = edgeAttributes;
+          } else {
+            commonAttributes = new Set([...commonAttributes].filter((x) => edgeAttributes.has(x)));
+          }
+        });
+    }
+
+    setOptions(Array.from(commonAttributes));
+  }, [element, dimension]);
+
+  return (
+    <div className="mb-2">
+      <Input type="dropdown" value={value} onChange={(val: string | number) => onChange(val)} options={options} disabled={disabled} />
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/encoding.tsx b/libs/shared/lib/vis/configuration/encodings/encoding.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c5177478075ecb93e43d9a81a94a8fb2076e0eb8
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/encoding.tsx
@@ -0,0 +1,86 @@
+import React, { useState, useEffect } from 'react';
+import { useAppDispatch, useVisEncodings } from '@graphpolaris/shared/lib/data-access';
+import { updateVisEncodings } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import Accessor from './accessor';
+import Info from '@graphpolaris/shared/lib/components/info';
+import { isEqual } from 'lodash-es';
+import Selector from './selector';
+import { EncodingTypes } from './encodings.types';
+
+export default function EncodingPanel() {
+  const dispatch = useAppDispatch();
+  const encodings = useVisEncodings();
+  const [encodingUpdates, setEncodingUpdates] = useState<{
+    [id: string]: {
+      marking: any;
+      accessorPath: string;
+    };
+  }>({});
+
+  useEffect(() => {
+    const newEncodings: EncodingTypes = Object.fromEntries(
+      Object.entries(encodings).map(([key, immutableEncoding]) => [
+        key,
+        {
+          ...(immutableEncoding || {}),
+          ...(encodingUpdates[key] || {}),
+        },
+      ])
+    );
+
+    if (!isEqual(encodings, newEncodings)) {
+      dispatch(updateVisEncodings(newEncodings));
+    }
+  }, [encodingUpdates, encodings, dispatch]);
+
+  const updateEncoding = (encoding: string, val: any, type: 'accessorPath' | 'marking') => {
+    setEncodingUpdates((prevState) => {
+      if (prevState) {
+        return {
+          ...prevState,
+          [encoding]: {
+            ...(prevState[encoding] || {}),
+            [type]: val,
+          },
+        };
+      } else {
+        return prevState;
+      }
+    });
+  };
+
+  return (
+    encodings && (
+      <div>
+        {Object.keys(encodings).map((k) => {
+          const item = encodings[k];
+          const value = encodingUpdates[k] ? encodingUpdates[k] : { marking: undefined, accessorPath: undefined };
+
+          return (
+            <div key={k} className="bg-secondary-50 p-2 m-1">
+              <div className="flex items-center justify-between">
+                <h1 className="text-xs">{item.label ? item.label : k}</h1>
+                {item.description && <Info tooltip={item.description} />}
+              </div>
+              <Accessor
+                value={value.accessorPath}
+                onChange={(value: string | number) => updateEncoding(k, value, 'accessorPath')}
+                element={item.element}
+                dimension={item.dimension ?? []}
+              />
+              {value.accessorPath && (
+                <Selector
+                  key={k}
+                  selectorType={item.selector}
+                  marking={value.marking}
+                  onChange={(value: any) => updateEncoding(k, value, 'marking')}
+                  accessorPath={value.accessorPath}
+                />
+              )}
+            </div>
+          );
+        })}
+      </div>
+    )
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/encodings.types.ts b/libs/shared/lib/vis/configuration/encodings/encodings.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ef1bc5b3684439bd10b40722669ae44554f67618
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/encodings.types.ts
@@ -0,0 +1,31 @@
+import { DimensionType } from '@graphpolaris/shared/lib/schema';
+import { EncodingSelector } from './selectors';
+import { ElementStats } from '@graphpolaris/shared/lib/data-access/utils/result-statistics';
+
+export type SelectorType = keyof typeof EncodingSelector;
+
+export type ElementType = 'node' | 'edge';
+
+export type EncodingType = 'label' | 'attribute';
+
+export type Encoding = {
+  label?: string;
+  marking?: any;
+  accessorPath?: string;
+  onChange?: (value: any) => void;
+  element: ElementType;
+  dimension?: DimensionType[];
+  selector: SelectorType;
+  description?: string;
+  condition?: () => boolean;
+};
+
+export type SelectorProps = {
+  marking: any;
+  onChange: (marking: any) => void;
+  statistics: ElementStats | undefined;
+};
+
+export type EncodingTypes = { [id: string]: Encoding };
+
+export type EncodingProps = { [K in keyof EncodingTypes]?: any };
diff --git a/libs/shared/lib/vis/configuration/encodings/index.ts b/libs/shared/lib/vis/configuration/encodings/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..056c6ae4da4344f331c8bf5368e0d41fd1a88994
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/index.ts
@@ -0,0 +1,2 @@
+export type { Encoding, EncodingTypes, EncodingProps } from './encodings.types';
+export { default as EncodingPanel } from './encoding';
diff --git a/libs/shared/lib/vis/configuration/encodings/selector.tsx b/libs/shared/lib/vis/configuration/encodings/selector.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7acf16bba682fee66ac9f0d310743857670f87b0
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selector.tsx
@@ -0,0 +1,29 @@
+import React, { useState, useEffect } from 'react';
+import { EncodingSelector } from './selectors';
+import { useGraphQueryResultStats } from '@graphpolaris/shared/lib/data-access';
+import { ElementStats } from '@graphpolaris/shared/lib/data-access/utils/result-statistics';
+
+type Props = {
+  selectorType: keyof typeof EncodingSelector;
+  marking: any;
+  onChange: (val: any) => void;
+  accessorPath: string;
+};
+
+export default function Selector({ selectorType, marking, onChange, accessorPath }: Props) {
+  const graphStatistics = useGraphQueryResultStats();
+  const [statistics, setStatistics] = useState<ElementStats | undefined>();
+
+  const SelectorComponent = EncodingSelector[selectorType];
+
+  const getStatisticsFromAccessorPath = (accessorPath: string): ElementStats | undefined => {
+    return undefined;
+  };
+
+  useEffect(() => {
+    const attributeStatistics = getStatisticsFromAccessorPath(accessorPath);
+    setStatistics(attributeStatistics);
+  }, []);
+
+  return <SelectorComponent marking={marking} onChange={onChange} statistics={statistics} />;
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/axis.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/axis.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..bdd786e4f1c808122e3caea18ecf938018a48359
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/axis.stories.tsx
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Axis from './axis';
+
+const Component: Meta<typeof Axis> = {
+  title: 'Components/Selectors/axis',
+  component: Axis,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const AxisStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+  return <Axis {...args} marking={value} onChange={setValue} />;
+};
+
+AxisStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/axis.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/axis.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..bd987f71de2e13f55bc3e4ecf79aa4ad7040585d
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/axis.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { SelectorProps } from '../encodings.types';
+
+export default function Axis({ marking, onChange }: SelectorProps) {
+  return (
+    <div>
+      <Input type="dropdown" label="Select axis" options={[]} value={marking} onChange={onChange} />
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/color.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/color.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..43169984c96cbbca5592f3d3307eb077990b6b7d
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/color.stories.tsx
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Color from './color';
+
+const Component: Meta<typeof Color> = {
+  title: 'Components/Selectors/color',
+  component: Color,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const ColorStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+  return <Color {...args} marking={value} onChange={setValue} />;
+};
+
+ColorStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/color.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/color.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4a21d5698ab03069cc02ae0589769cb48d5c1d46
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/color.tsx
@@ -0,0 +1,16 @@
+import React, { useState } from 'react';
+import { SelectorProps } from '../encodings.types';
+import DropdownColorLegend from '@graphpolaris/shared/lib/components/colorComponents/colorDropdown';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+
+export default function Color({ marking, onChange }: SelectorProps) {
+  const [colors, setColors] = useState<any>(null);
+  const [custom, setCustom] = useState<boolean>(false);
+
+  return (
+    <div>
+      <Input type="boolean" label="Custom palette?" value={custom} onChange={(val: boolean) => setCustom(val)} />
+      <DropdownColorLegend value={colors} onChange={setColors} />
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/index.ts b/libs/shared/lib/vis/configuration/encodings/selectors/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..14964560cc76dc9467199fcebe6dd68fa21064b0
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/index.ts
@@ -0,0 +1,15 @@
+import Color from './color';
+import Shape from './shape';
+import Size from './size';
+import Axis from './axis';
+import Opacity from './opacity';
+
+export const EncodingSelector = {
+  Color: Color,
+  Shape: Shape,
+  Size: Size,
+  Axis: Axis,
+  Opacity: Opacity,
+};
+
+// Inspiration: https://uwdata.github.io/visualization-curriculum/altair_marks_encoding.html
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/opacity.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/opacity.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2f3f3a67cce52e8746a519fcfb0c6de0571a9358
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/opacity.stories.tsx
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Opacity from './opacity';
+
+const Component: Meta<typeof Opacity> = {
+  title: 'Components/Selectors/opacity',
+  component: Opacity,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const OpacityStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+  return <Opacity {...args} marking={value} onChange={setValue} />;
+};
+
+OpacityStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/opacity.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/opacity.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..40d85e5c8428af7323ab5433da9e67b1dcc570ad
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/opacity.tsx
@@ -0,0 +1,15 @@
+import React, { useState } from 'react';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { SelectorProps } from '../encodings.types';
+
+export default function Opacity({ marking, onChange }: SelectorProps) {
+  const [custom, setCustom] = useState<boolean>(false);
+
+  return (
+    <div>
+      <Input type="dropdown" label="Select axis" options={[]} value={marking} onChange={onChange} />
+      <Input type="boolean" label="Custom input" value={custom} onChange={(value) => setCustom(value)} />
+      {custom && <Input type="slider" label="Select value" value={marking} onChange={onChange} min={0} max={100} step={10} />}
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/shape.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/shape.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f2a1c62ca10b4f92fc76711591394559cc5130db
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/shape.stories.tsx
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Shape from './shape';
+
+const Component: Meta<typeof Shape> = {
+  title: 'Components/Selectors/shape',
+  component: Shape,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const ShapeStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+  return <Shape {...args} marking={value} onChange={setValue} />;
+};
+
+ShapeStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/shape.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/shape.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ae3b6ae45efdf6ff32a997fe0b99cbf63a8a66ed
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/shape.tsx
@@ -0,0 +1,6 @@
+import React from 'react';
+import { SelectorProps } from '../encodings.types';
+
+export default function Shape({ marking }: SelectorProps) {
+  return <div>shape</div>;
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/size.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/size.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fc3d1ea2d5c23c591f41c738bad778ea98a9eb63
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/size.stories.tsx
@@ -0,0 +1,22 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Size from './size';
+
+const Component: Meta<typeof Size> = {
+  title: 'Components/Selectors/size',
+  component: Size,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const SizeStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+
+  return <Size {...args} marking={value} onChange={setValue} />;
+};
+
+SizeStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/size.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/size.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..aa7e1de990b9c2afe8f040fa87c97e1e6e5ddf9f
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/size.tsx
@@ -0,0 +1,15 @@
+import React, { useState } from 'react';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { SelectorProps } from '../encodings.types';
+
+export default function Size({ marking, onChange }: SelectorProps) {
+  const [item, setItem] = useState<string>('');
+  const [custom, setCustom] = useState<boolean>(false);
+
+  return (
+    <div>
+      <Input type="boolean" label="Custom input" value={custom} onChange={(value) => setCustom(value)} />
+      {custom && <Input type="slider" label="Select value" value={marking} onChange={onChange} min={0} max={100} step={10} />}
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/index.ts b/libs/shared/lib/vis/configuration/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3ded492f1ed3e4796ad053193fbce2df8a5ec6c9
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/index.ts
@@ -0,0 +1 @@
+export { default as VisualizationDialog } from './panel/panel';
diff --git a/libs/shared/lib/vis/configuration/interactions/index.ts b/libs/shared/lib/vis/configuration/interactions/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61687e06e2fff5a8911823cc0ee1359e047f91e5
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/interactions/index.ts
@@ -0,0 +1,2 @@
+export type { Interaction, InteractionTypes, InteractionProps } from './interaction.types';
+export { default as InteractionPanel } from './interaction';
diff --git a/libs/shared/lib/vis/configuration/interactions/interaction.tsx b/libs/shared/lib/vis/configuration/interactions/interaction.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cd40f451bd5c99ebc08899be75abb4fdd7295b53
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/interactions/interaction.tsx
@@ -0,0 +1,24 @@
+import React, { useEffect, useState } from 'react';
+import { useAppDispatch, useVisInteractions } from '@graphpolaris/shared/lib/data-access';
+import { updateVisInteractions } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+
+export default function InteractionPanel() {
+  const dispatch = useAppDispatch();
+  const interactions = useVisInteractions();
+  const [interactionUpdates, setInteractionUpdates] = useState(interactions);
+
+  useEffect(() => {
+    if (interactions !== interactionUpdates) {
+      dispatch(updateVisInteractions(interactionUpdates));
+      console.debug('Interactions updated');
+    }
+  }, [interactionUpdates]);
+
+  return (
+    interactions && (
+      <div>
+        <p>Interaction settings</p>
+      </div>
+    )
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/interactions/interaction.types.ts b/libs/shared/lib/vis/configuration/interactions/interaction.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bb81b490925dd4e432865958c757150bdef092e8
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/interactions/interaction.types.ts
@@ -0,0 +1,7 @@
+import { InputProps } from '@graphpolaris/shared/lib/components/inputs';
+
+export type Interaction = InputProps & { condition?: (config: Record<string, any>) => boolean };
+
+export type InteractionTypes = { [id: string]: Interaction };
+
+export type InteractionProps = { [K in keyof InteractionTypes]?: any };
diff --git a/libs/shared/lib/vis/configuration/panel/panel-header.tsx b/libs/shared/lib/vis/configuration/panel/panel-header.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..86e4340951bf7eab7d752e8e1cdedd43c8bbf3fb
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/panel/panel-header.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { FormTitle } from '@graphpolaris/shared/lib/components/forms';
+import { AutoAwesome, CandlestickChart, LockOpen, Settings } from '@mui/icons-material';
+import { useVisualizationState } from '@graphpolaris/shared/lib/data-access';
+import TabItem from './tab-item';
+
+type Props = {
+  onClose: any;
+  activeTab: number;
+  setActiveTab: any;
+};
+
+export default function PanelHeader({ onClose, activeTab, setActiveTab }: Props) {
+  const vis = useVisualizationState();
+
+  const encodings = vis.settings[vis.activeVisualization]?.encodings;
+  const encodingsExist = encodings && Object.keys(encodings).length > 0;
+  const settings = vis.settings[vis.activeVisualization]?.settings;
+  const settingsExist = settings && Object.keys(settings).length > 0;
+  const interactions = vis.settings[vis.activeVisualization]?.interactions;
+  const interactionsExist = interactions && Object.keys(interactions).length > 0;
+
+  return (
+    <div className="flex flex-col pt-2 bg-secondary-100">
+      <FormTitle title="Settings" onClose={onClose} />
+      <ul className="flex flex-wrap pt-4 pl-5 text-sm font-medium text-center text-gray-500 dark:text-gray-400">
+        {encodingsExist && <TabItem active={activeTab === 0} onClick={() => setActiveTab(0)} icon={<AutoAwesome />} tooltip="Encodings" />}
+        {settingsExist && <TabItem active={activeTab === 1} onClick={() => setActiveTab(1)} icon={<Settings />} tooltip="Settings" />}
+        {interactionsExist && (
+          <TabItem active={activeTab === 2} onClick={() => setActiveTab(2)} icon={<CandlestickChart />} tooltip="Interactions" />
+        )}
+        {(settingsExist || encodingsExist || interactionsExist) && (
+          <TabItem active={activeTab === 3} onClick={() => setActiveTab(3)} icon={<LockOpen />} tooltip="Advanced" />
+        )}
+      </ul>
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/panel/panel.tsx b/libs/shared/lib/vis/configuration/panel/panel.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b257949832a13ae79582ac03a7ed41d59ad230a8
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/panel/panel.tsx
@@ -0,0 +1,37 @@
+import React, { useState } from 'react';
+import { FormActions, FormBody, FormCard, FormDiv, FormHBar } from '@graphpolaris/shared/lib/components/forms';
+import { DialogProps } from '@graphpolaris/shared/lib/components/Dialog';
+import PanelHeader from './panel-header';
+import { SettingsPanel } from '../settings';
+import { AdvancedPanel } from '../advanced';
+import { EncodingPanel } from '../encodings';
+import { InteractionPanel } from '../interactions';
+
+export default function VisualizationDialog(props: DialogProps) {
+  const [activeTab, setActiveTab] = useState<number>(1);
+
+  return (
+    <>
+      {props.open && (
+        <FormDiv>
+          <FormCard>
+            <FormBody
+              onSubmit={(e) => {
+                e.preventDefault();
+                props.onClose();
+              }}
+            >
+              <PanelHeader onClose={props.onClose} activeTab={activeTab} setActiveTab={setActiveTab} />
+              {activeTab === 0 && <EncodingPanel />}
+              {activeTab === 1 && <SettingsPanel />}
+              {activeTab === 2 && <InteractionPanel />}
+              {activeTab === 3 && <AdvancedPanel />}
+              <FormHBar />
+              <FormActions onClose={props.onClose} />
+            </FormBody>
+          </FormCard>
+        </FormDiv>
+      )}
+    </>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/panel/tab-item.tsx b/libs/shared/lib/vis/configuration/panel/tab-item.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..72d055dcd2caeca56da61b753d2e914b9cea8ccb
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/panel/tab-item.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import Tooltip from '@graphpolaris/shared/lib/components/tooltip';
+
+type TabItemProps = {
+  active: boolean;
+  onClick: () => void;
+  icon: JSX.Element;
+  tooltip: string;
+};
+
+export default function TabItem({ active, onClick, icon, tooltip }: TabItemProps) {
+  return (
+    <li
+      className={`me-2 inline-block bg-secondary-100 cursor-pointer ${active && 'border-b-2 border-primary-200 text-primary-200'}`}
+      onClick={onClick}
+    >
+      <Tooltip tooltip={tooltip}>{icon}</Tooltip>
+    </li>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/settings/index.ts b/libs/shared/lib/vis/configuration/settings/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..825e0031f676fb1604549de65832fce77fe9de90
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/settings/index.ts
@@ -0,0 +1,2 @@
+export type { Setting, SettingTypes, SettingProps } from './settings.types';
+export { default as SettingsPanel } from './settings';
diff --git a/libs/shared/lib/vis/configuration/settings/settings.tsx b/libs/shared/lib/vis/configuration/settings/settings.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..bf4a9408a2219a95e011979f1b903df33213b34d
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/settings/settings.tsx
@@ -0,0 +1,84 @@
+import React, { useState, useEffect } from 'react';
+import { useAppDispatch, useGeneralSettings, useVisSettings, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
+import { globalConfigTypes } from '../../types';
+import { FormControl } from '@graphpolaris/shared/lib/components/forms';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { updateGeneralSettings, updateVisSettings } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+
+export default function SettingsPanel() {
+  const dispatch = useAppDispatch();
+  const vis = useVisualizationState();
+
+  const generalSettings = useGeneralSettings();
+  const [settingsGeneral, setSettingsGeneral] = useState<globalConfigTypes>(generalSettings);
+
+  const visSettings = useVisSettings();
+  const [settingsSpecific, setSettingsSpecific] = useState(visSettings);
+
+  useEffect(() => {
+    if (settingsGeneral !== generalSettings) {
+      dispatch(updateGeneralSettings(settingsGeneral));
+    }
+  }, [settingsGeneral]);
+
+  useEffect(() => {
+    if (settingsSpecific !== visSettings) {
+      dispatch(updateVisSettings(settingsSpecific));
+    }
+  }, [settingsSpecific]);
+
+  useEffect(() => {
+    if (visSettings !== settingsSpecific) {
+      setSettingsSpecific(visSettings);
+    }
+  }, [vis.activeVisualization]);
+
+  return (
+    generalSettings &&
+    visSettings && (
+      <div>
+        <div>
+          {Object.keys(settingsGeneral).map((val) => (
+            <FormControl key={val}>
+              <Input
+                {...settingsGeneral[val]}
+                value={vis.settings.general[val].value as any}
+                onChange={(value: any) => {
+                  setSettingsGeneral((prevSettings) => ({
+                    ...prevSettings,
+                    [val]: { ...prevSettings[val], value: value },
+                  }));
+                }}
+              />
+            </FormControl>
+          ))}
+        </div>
+
+        <div>
+          {Object.keys(settingsSpecific).map((val) => {
+            const currentSetting = settingsSpecific[val];
+            const shouldShowSetting = currentSetting.condition ? currentSetting.condition?.(settingsSpecific.settings) : true;
+            return (
+              shouldShowSetting && (
+                <div key={val} className="bg-secondary-50 p-2 m-2">
+                  <FormControl>
+                    <Input
+                      {...settingsSpecific[val]}
+                      value={settingsSpecific[val]?.value as any}
+                      onChange={(value: any) => {
+                        setSettingsSpecific((prevSettings) => ({
+                          ...prevSettings,
+                          [val]: { ...prevSettings[val], value: value },
+                        }));
+                      }}
+                    />
+                  </FormControl>
+                </div>
+              )
+            );
+          })}
+        </div>
+      </div>
+    )
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/settings/settings.types.ts b/libs/shared/lib/vis/configuration/settings/settings.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c93ba813d26201c93681f9346a39fd64914d5a9
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/settings/settings.types.ts
@@ -0,0 +1,10 @@
+import { InputProps } from '@graphpolaris/shared/lib/components/inputs';
+
+export type Setting = InputProps & {
+  condition?: (config: Record<string, any>) => boolean;
+  description?: string;
+};
+
+export type SettingTypes = { [id: string]: Setting };
+
+export type SettingProps = { [K in keyof SettingTypes]: any };
diff --git a/libs/shared/lib/vis/documentation.mdx b/libs/shared/lib/vis/documentation.mdx
deleted file mode 100644
index 34ce016ac09d99ac5a2c8652283d1356635c1fdb..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/documentation.mdx
+++ /dev/null
@@ -1,152 +0,0 @@
-import { Meta, Unstyled } from '@storybook/blocks';
-
-<Meta title="Visualizations/Implementation" />
-
-# Visualization documentation
-
-## Contents
-
-1. [Introduction](#Introduction)
-2. [Overview](#Overview)
-3. [Implementation](#Implementation)
-4. [Props](#Props)
-
-## Introduction
-
-This is the documentation for implementing visualizations within the GraphPolaris system.
-
-## Overview
-
-The basic code implementation looks like this:
-
-```jsx
-import React from 'react';
-import { VisualizationPropTypes, VISComponentType, localConfigSchemaType } from '@graphpolaris/shared/lib/vis/Types';
-
-/**
- * Props specific to the Visualization component.
- */
-export type VisualizationProps = {};
-
-/**
- * Visualization Component
- * @param {object} props - Props for the Visualization component.
- * @param {GraphQueryResult} props.data - Queried data from the database.
- * @param {object} props.schema - Database schema.
- * @param {any} props.ml - Machine-learning results.
- * @param {function} props.dispatch - Redux dispatch for state management.
- * @param {object} props.globalConfig - Global configurations for the panel (applicable to all visualizations).
- * @param {object} props.localConfig - Local configuration props unique to this visualization.
- * @returns {JSX.Element} - Rendered content of the visualization.
- */
-export const Visualization = ({ data, schema, ml, dispatch, globalConfig, localConfig }: VisualizationPropTypes) => {
-  // Perform additional processing specific to this visualization using the provided props
-
-  return (
-    // Rendered visualization content
-  );
-};
-
-/**
- * Schema for local configuration attributes.
- */
-const localConfigSchema: localConfigSchemaType = {};
-
-/**
- * Visualization Component Object
- * Contains information and configurations for the exported visualization component.
- */
-export const VisualizationComponent: VISComponentType = {
-  displayName: 'Visualization',
-  VIS: Visualization,
-  localConfigSchema: localConfigSchema,
-};
-```
-
-## Implementation
-
-### VisualizationProps
-
-This is where the types are defined for the local configuration of the visualization. It has to define all values that are in the localConfigSchema. This is exported and used in the visualization factory for the local config types.
-
-Example:
-
-```jsx
-export type TableProps = {
-  showBarplot: boolean,
-  itemsPerPage: number,
-};
-```
-
-### localConfigSchema
-
-Here are the parameters set that can be changed by a user. These values are displayed in the settings panel. It needs to be in the form of a reusable input component.
-
-Example:
-
-```jsx
-const localConfigSchema: localConfigSchemaType = {
-  showBarplot: {
-    value: true,
-    type: 'boolean',
-    label: 'Show barplot',
-  },
-  itemsPerPage: {
-    value: 10,
-    type: 'dropdown',
-    label: 'Items per page',
-    options: [10, 20, 30],
-  },
-};
-```
-
-#### Conditional rendering
-
-It is possible to only show certain settings based on a condition.
-
-```jsx
-const localConfigSchema: localConfigSchemaType = {
-  showBarplot: {
-    value: true,
-    type: 'boolean',
-    label: 'Show barplot',
-  },
-  itemsPerPage: {
-    value: 10,
-    type: 'dropdown',
-    label: 'Items per page',
-    options: [10, 20, 30],
-    condition: (localConf) => localConf.showBarplot.value === false,
-  },
-};
-```
-
-### VisualizationComponent
-
-The visualization component is what gets exported and is used in the factory. It must have a displayName, VIS and localConfigSchema.
-
-## Props
-
-### Data prop
-
-Contains the queried data from the database.
-
-### Schema prop
-
-Contains the database schema.
-
-### ML prop
-
-Contains the Machine-Learning results.
-
-### Dispatch prop
-
-Contains the dispatch for the redux state.
-
-### Global config prop
-
-Contains the global configurations for the panel that is the same for every visualization.
-
-### Local config prop
-
-Contains the local config props that are unique per visualization.
diff --git a/libs/shared/lib/vis/index.ts b/libs/shared/lib/vis/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92ed4302df37a1cab0b048c461ceb6e0efbac8dd
--- /dev/null
+++ b/libs/shared/lib/vis/index.ts
@@ -0,0 +1 @@
+export * from './visualizationPanel';
diff --git a/libs/shared/lib/vis/index.tsx b/libs/shared/lib/vis/index.tsx
deleted file mode 100644
index cb72178b7c0ed3b9f187a3d00ec11d3ad18f5700..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/index.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import React, { Suspense, useEffect, useState } from 'react';
-import { useAppDispatch } from '@graphpolaris/shared/lib/data-access';
-import { addVisualization } from '../data-access/store/visualizationSlice';
-import {
-  useGraphQueryResult,
-  useML,
-  useSchemaGraph,
-  useVisualizationSettings,
-  useVisualizationState,
-} from '@graphpolaris/shared/lib/data-access/store/hooks';
-import { localConfigPropTypes, globalConfigPropTypes, VISComponentType } from './types';
-
-// export * from './rawjsonvis';
-// export * from './nodelink/nodelinkvis';
-// export * from './paohvis/paohvis';
-// export * from './semanticsubstrates/semanticsubstrates';
-// export * from './table_vis/tableVis';
-// export * from './mapvis/mapvis';
-
-export const Visualizations: Record<string, Function> = {
-  TableVis: () => import('./visualizations/table_vis/tableVis'),
-  PaohVis: () => import('./visualizations/paohvis/paohvis'),
-  RawJSONVis: () => import('./visualizations/rawjsonvis/rawjsonvis'),
-  NodeLinkVis: () => import('./visualizations/nodelink/index'),
-  // MapVis: () => import(''),
-  MatrixVis: () => import('./visualizations/matrix/matrixvis'),
-};
-
-export const VisualizationComponent = () => {
-  const dispatch = useAppDispatch();
-  const vis = useVisualizationState();
-  const settings = useVisualizationSettings();
-  const graphQueryResult = useGraphQueryResult();
-  const schema = useSchemaGraph();
-  const ml = useML();
-
-  const [visualizationComponent, setVisualizationComponent] = useState<VISComponentType>();
-
-  useEffect(() => {
-    if (vis.activeVisualization && vis.activeVisualization in Visualizations) {
-      Visualizations[vis.activeVisualization]().then((r: any) => {
-        setVisualizationComponent(r.default);
-      });
-    }
-  }, [vis.activeVisualization]);
-
-  useEffect(() => {
-    if (visualizationComponent) {
-      dispatch(addVisualization({ id: visualizationComponent.displayName, settings: visualizationComponent.localConfigSchema }));
-    }
-  }, [vis.activeVisualization]);
-
-  if (!VisualizationComponent) {
-    return <div className="w-full h-full flex items-center justify-center">Visualization not found</div>;
-  }
-
-  const globalConfig: globalConfigPropTypes =
-    settings.general &&
-    Object.keys(settings.general).reduce((propsObject, val) => {
-      return {
-        ...propsObject,
-        [val]: vis.settings.general[val].value,
-      };
-    }, {});
-
-  const displayName = vis.activeVisualization as keyof typeof Visualizations;
-
-  const localConfig: localConfigPropTypes<typeof displayName> =
-    settings[vis.activeVisualization] &&
-    (Object.keys(settings[vis.activeVisualization]).reduce((propsObject, val) => {
-      return {
-        ...propsObject,
-        [val]: vis.settings[vis.activeVisualization][val].value,
-      };
-    }, {}) as localConfigPropTypes<typeof displayName>);
-
-  return (
-    localConfig &&
-    visualizationComponent && (
-      <div className="w-full h-full">
-        <visualizationComponent.VIS
-          data={graphQueryResult}
-          schema={schema}
-          ml={ml}
-          dispatch={dispatch}
-          globalConfig={globalConfig}
-          localConfig={localConfig}
-        />
-      </div>
-    )
-  );
-};
diff --git a/libs/shared/lib/vis/panel/dialog.tsx b/libs/shared/lib/vis/panel/dialog.tsx
deleted file mode 100644
index 5902931be45b358bd6b4eeeeb90371be6997fb9c..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/panel/dialog.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { FormActions, FormBody, FormCard, FormControl, FormDiv, FormHBar, FormTitle } from '@graphpolaris/shared/lib/components/forms';
-import { DialogProps } from '@graphpolaris/shared/lib/components/Dialog';
-import { useAppDispatch, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
-import { updateGeneralSettings, updateVisualizationSettings } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
-import Input from '@graphpolaris/shared/lib/components/inputs';
-import { globalConfigSchemaTypes, localConfigSchemaType } from '@graphpolaris/shared/lib/vis/types';
-
-export default function VisualizationDialog(props: DialogProps) {
-  const dispatch = useAppDispatch();
-  const vis = useVisualizationState();
-  const [settingsGeneral, setSettingsGeneral] = useState<globalConfigSchemaTypes>(vis.settings.general);
-  const [settingsSpecific, setSettingsSpecific] = useState<localConfigSchemaType>(vis.settings[vis.activeVisualization]);
-
-  useEffect(() => {
-    if (vis.settings.general !== settingsGeneral) {
-      dispatch(updateGeneralSettings(settingsGeneral));
-    }
-  }, [settingsGeneral]);
-
-  useEffect(() => {
-    if (vis.settings[vis.activeVisualization] !== settingsSpecific) {
-      dispatch(
-        updateVisualizationSettings({
-          id: vis.activeVisualization,
-          settings: settingsSpecific,
-        }),
-      );
-    }
-  }, [settingsSpecific]);
-
-  useEffect(() => {
-    setSettingsSpecific(vis.settings[vis.activeVisualization]);
-  }, [vis.settings[vis.activeVisualization]]);
-
-  return (
-    <>
-      {props.open && (
-        <FormDiv>
-          <FormCard>
-            <FormBody
-              onSubmit={(e) => {
-                e.preventDefault();
-                props.onClose();
-              }}
-            >
-              <FormTitle title="Settings" onClose={props.onClose} />
-              <FormHBar />
-
-              {settingsGeneral && (
-                <>
-                  {Object.keys(settingsGeneral).map((val) => (
-                    <FormControl key={val}>
-                      <Input
-                        {...settingsGeneral[val]}
-                        value={vis.settings.general[val].value as any}
-                        onChange={(value: any) => {
-                          setSettingsGeneral((prevSettings) => ({
-                            ...prevSettings,
-                            [val]: { ...prevSettings[val], value: value },
-                          }));
-                        }}
-                      />
-                    </FormControl>
-                  ))}
-                </>
-              )}
-
-              {Object.keys(settingsSpecific).length > 0 && (
-                <>
-                  <FormHBar />
-                  {Object.keys(settingsSpecific).map((val) => {
-                    const currentSetting = settingsSpecific[val];
-                    const shouldShowSetting = currentSetting.condition ? currentSetting.condition?.(settingsSpecific) : true;
-                    return (
-                      shouldShowSetting && (
-                        <FormControl key={val}>
-                          <Input
-                            {...settingsSpecific[val]}
-                            value={settingsSpecific[val]?.value as any}
-                            onChange={(value: any) => {
-                              setSettingsSpecific((prevSettings) => ({
-                                ...prevSettings,
-                                [val]: { ...prevSettings[val], value: value },
-                              }));
-                            }}
-                          />
-                        </FormControl>
-                      )
-                    );
-                  })}
-                </>
-              )}
-
-              <FormHBar />
-              <FormActions onClose={props.onClose} />
-            </FormBody>
-          </FormCard>
-        </FormDiv>
-      )}
-    </>
-  );
-}
diff --git a/libs/shared/lib/vis/panel/index.ts b/libs/shared/lib/vis/panel/index.ts
deleted file mode 100644
index bda371666afe16f8bf29ae770e9267f20d40631a..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/panel/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './visualization';
-
-import { VisualizationPanel } from './visualization';
-export default VisualizationPanel;
diff --git a/libs/shared/lib/vis/panel/visualization.tsx b/libs/shared/lib/vis/panel/visualization.tsx
deleted file mode 100644
index 67557ff45a7483df110012e446da3a042fb53997..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/panel/visualization.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import React, { useState } from 'react';
-import { useAppDispatch, useGraphQueryResult, useQuerybuilderGraph, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
-import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
-import { DropdownItem, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
-import ControlContainer from '@graphpolaris/shared/lib/components/controls';
-import { Button } from '@graphpolaris/shared/lib/components/buttons';
-import { VisualizationComponent } from '../';
-import VisualizationDialog from './dialog';
-import { Settings, Apps } from '@mui/icons-material';
-
-export const VisualizationPanel = () => {
-  const vis = useVisualizationState();
-  const graphQueryResult = useGraphQueryResult();
-  const query = useQuerybuilderGraph();
-  const dispatch = useAppDispatch();
-  const [visDropdownOpen, setVisDropdownOpen] = useState<boolean>(false);
-  const [showVisSettings, setShowVisSettings] = useState<boolean>(false);
-
-  return (
-    <div className="vis-panel h-full w-full overflow-y-auto" style={graphQueryResult.nodes.length === 0 ? { overflow: 'hidden' } : {}}>
-      <VisualizationDialog open={showVisSettings} onClose={() => setShowVisSettings(false)} />
-      <div className="sticky top-0 flex items-center justify-between z-[2] py-0 px-2 bg-secondary-100  border-b border-secondary-200">
-        <h1 className="text-xs font-semibold text-secondary-800">{vis.activeVisualization} visualization</h1>
-        <ControlContainer>
-          <Button
-            type="secondary"
-            variant="ghost"
-            size="xs"
-            iconComponent={<Settings />}
-            onClick={() => {
-              setShowVisSettings(!showVisSettings);
-            }}
-          />
-          <Button
-            type="secondary"
-            variant="ghost"
-            size="xs"
-            iconComponent={<Apps />}
-            onClick={() => {
-              setVisDropdownOpen(!visDropdownOpen);
-            }}
-          />
-          {visDropdownOpen && (
-            <DropdownItemContainer align="top-6 right-6">
-              {Object.keys(Visualizations).map((key) => (
-                <DropdownItem
-                  key={key}
-                  value={key}
-                  onClick={() => {
-                    setVisDropdownOpen(false);
-                    dispatch(setActiveVisualization(Visualizations[key as keyof typeof Visualizations]));
-                  }}
-                />
-              ))}
-            </DropdownItemContainer>
-          )}
-        </ControlContainer>
-      </div>
-
-      {graphQueryResult.queryingBackend ? (
-        <div className="w-full h-full flex flex-col items-center justify-center overflow-hidden">
-          <LoadingSpinner>Querying backend...</LoadingSpinner>
-        </div>
-      ) : graphQueryResult.nodes.length === 0 ? (
-        <div className="w-full h-full flex flex-col items-center justify-center">
-          <p>No data available to be shown</p>
-          {query.nodes.length > 0 ? <p>Query resulted in empty dataset</p> : <p>Query for data to visualize</p>}
-        </div>
-      ) : (
-        <div className="w-full h-full">
-          <VisualizationComponent />
-        </div>
-      )}
-    </div>
-  );
-};
diff --git a/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx b/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx
index 965d7a1d3a4b4457046c1df67a9fd0e1f8ebebe0..611d8d1208a3ce5e43e3770d27a5bd1e91397edc 100644
--- a/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx
+++ b/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx
@@ -3,7 +3,7 @@
  * Utrecht University within the Software Project course.
  * © Copyright Utrecht University (Department of Information and Computing Sciences)
  */
-import { GraphType, LinkType, NodeType } from '../visualizations/nodelink/types';
+import { GraphType, LinkType, NodeType } from '../visualizations/nodelinkvis/types';
 import { Edge, Node, GraphQueryResult } from '../../data-access/store';
 import { ML } from '../../data-access/store/mlSlice';
 /** ResultNodeLinkParserUseCase implements methods to parse and translate websocket messages from the backend into a GraphType. */
diff --git a/libs/shared/lib/vis/types.ts b/libs/shared/lib/vis/types.ts
index ef3523875ffe9b3ca2b2d0c423503bec4d8a6616..cf79a63d5f46d182445ac9ce591e329258d58bcc 100644
--- a/libs/shared/lib/vis/types.ts
+++ b/libs/shared/lib/vis/types.ts
@@ -1,49 +1,39 @@
-import { NodeLinkProps, PaohVisProps, RawJSONVisProps, SemanticSubstratesProps, TableProps } from './visualizations';
 import { GraphQueryResult } from '../data-access/store/graphQueryResultSlice';
 import { ML } from '../data-access/store/mlSlice';
 import { SchemaGraph } from '../schema';
 import type { AppDispatch } from '../data-access';
 import { InputProps } from '../components/inputs';
 import { FC } from 'react';
-import { Visualizations } from '.';
+import { EncodingProps, EncodingTypes } from './configuration/encodings';
+import { SettingProps, SettingTypes } from './configuration/settings';
+import { InteractionProps, InteractionTypes } from './configuration/interactions';
+import { Visualizations } from './visualizationManager';
 
-export type settingConifgTypes = InputProps;
+export type globalConfigTypes = { [id: string]: InputProps };
 
-export type globalConfigSchemaTypes = {
-  [id: string]: settingConifgTypes;
-};
-
-export type globalConfigPropTypes = {
-  [id: string]: any;
-};
+export type globalConfigPropTypes = { [K in keyof globalConfigTypes]: any };
 
-export type localConfigSchemaType = {
-  [id: string]: settingConifgTypes & { condition?: (config: Record<string, any>) => boolean };
+export type VisualizationConfiguration = {
+  settings?: SettingTypes;
+  encodings?: EncodingTypes;
+  interactions?: InteractionTypes;
 };
 
-export type localConfigPropTypes<T extends keyof typeof Visualizations> = T extends 'TableVis'
-  ? TableProps
-  : T extends 'PaohVis'
-    ? PaohVisProps
-    : T extends 'RawJSONVis'
-      ? RawJSONVisProps
-      : T extends 'SemanticSubstrates'
-        ? SemanticSubstratesProps
-        : T extends 'NodeLinkVis'
-          ? NodeLinkProps
-          : never;
-
 export type VISComponentType = {
-  displayName: string;
-  VIS: any;
-  localConfigSchema: localConfigSchemaType;
+  displayName: keyof typeof Visualizations;
+  VIS: FC<any>;
+  settings?: SettingTypes;
+  encodings?: EncodingTypes;
+  interactions?: InteractionTypes;
 };
 
-export type VisualizationPropTypes<T extends keyof typeof Visualizations> = {
+export type VisualizationPropTypes = {
   data: GraphQueryResult;
   schema: SchemaGraph;
   ml: ML;
   dispatch: AppDispatch;
   globalConfig: globalConfigPropTypes;
-  localConfig: localConfigPropTypes<T>;
+  settings: SettingProps;
+  encodings?: EncodingProps;
+  interactions?: InteractionProps;
 };
diff --git a/libs/shared/lib/vis/visualizationManager.tsx b/libs/shared/lib/vis/visualizationManager.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..67037144021e5825d13d4e4b4f59e7cf193d9aa0
--- /dev/null
+++ b/libs/shared/lib/vis/visualizationManager.tsx
@@ -0,0 +1,108 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { useAppDispatch } from '@graphpolaris/shared/lib/data-access';
+import { addVisualization } from '../data-access/store/visualizationSlice';
+import {
+  useGraphQueryResult,
+  useML,
+  useSchemaGraph,
+  useVisualizationSettings,
+  useVisualizationState,
+} from '@graphpolaris/shared/lib/data-access/store/hooks';
+import { VISComponentType, globalConfigPropTypes } from './types';
+
+export const Visualizations: Record<string, Function> = {
+  TableVis: () => import('./visualizations/tablevis/tablevis'),
+  PaohVis: () => import('./visualizations/paohvis/paohvis'),
+  RawJSONVis: () => import('./visualizations/rawjsonvis/rawjsonvis'),
+  NodeLinkVis: () => import('./visualizations/nodelinkvis/nodelinkvis'),
+  // MapVis: () => import(''),
+  MatrixVis: () => import('./visualizations/matrixvis/matrixvis'),
+};
+
+export const VisualizationManager = () => {
+  const dispatch = useAppDispatch();
+  const vis = useVisualizationState();
+  const settings = useVisualizationSettings();
+  const graphQueryResult = useGraphQueryResult();
+  const schema = useSchemaGraph();
+  const ml = useML();
+
+  const [visualizationComponent, setVisualizationComponent] = useState<VISComponentType>();
+
+  useEffect(() => {
+    if (vis.activeVisualization && vis.activeVisualization in Visualizations) {
+      Visualizations[vis.activeVisualization]().then((r: any) => {
+        setVisualizationComponent(r.default);
+      });
+    }
+  }, [vis.activeVisualization]);
+
+  useEffect(() => {
+    if (visualizationComponent) {
+      const { displayName, settings = {}, encodings = {}, interactions = {} } = visualizationComponent;
+      dispatch(addVisualization({ id: displayName, settings, encodings, interactions }));
+    }
+  }, [vis.activeVisualization]);
+
+  if (!visualizationComponent) {
+    return <></>;
+  }
+
+  const globalConfig: globalConfigPropTypes = settings.general
+    ? Object.keys(settings.general).reduce((propsObject, val) => {
+        return {
+          ...propsObject,
+          [val]: vis.settings.general[val].value,
+        };
+      }, {})
+    : {};
+
+  let visSettings = {};
+  let visEncodings = {};
+  let visInteractions = {};
+
+  if (settings[vis.activeVisualization]) {
+    visSettings = Object.keys(settings[vis.activeVisualization]?.settings ?? {}).reduce((propsObject, val) => {
+      return {
+        ...propsObject,
+        [val]: vis.settings[vis.activeVisualization]?.settings?.[val]?.value,
+      };
+    }, {});
+
+    visEncodings = Object.keys(settings[vis.activeVisualization]?.encodings ?? {}).reduce((propsObject, val) => {
+      return {
+        ...propsObject,
+        [val]: vis.settings[vis.activeVisualization]?.encodings?.[val]?.marking,
+      };
+    }, {});
+
+    visInteractions = Object.keys(settings[vis.activeVisualization]?.interactions ?? {}).reduce((propsObject, val) => {
+      return {
+        ...propsObject,
+        [val]: vis.settings[vis.activeVisualization]?.interactions?.[val]?.value,
+      };
+    }, {});
+  }
+
+  try {
+    return (
+      visSettings &&
+      visualizationComponent && (
+        <div className="w-full h-full">
+          <visualizationComponent.VIS
+            data={graphQueryResult}
+            schema={schema}
+            ml={ml}
+            dispatch={dispatch}
+            globalConfig={globalConfig}
+            settings={visSettings}
+            encodings={visEncodings}
+            interactions={visInteractions}
+          />
+        </div>
+      )
+    );
+  } catch (error) {
+    return <div className="w-full h-full flex items-center justify-center">Something went wrong in the visualization component.</div>;
+  }
+};
diff --git a/apps/web/src/app/panels/Visualization.tsx b/libs/shared/lib/vis/visualizationPanel.tsx
similarity index 60%
rename from apps/web/src/app/panels/Visualization.tsx
rename to libs/shared/lib/vis/visualizationPanel.tsx
index 3a28806cba5a909280649ffd681ab9066ba81b87..0cb6f554c3bdf0a83db3c50e8ad95c727e83aa85 100644
--- a/apps/web/src/app/panels/Visualization.tsx
+++ b/libs/shared/lib/vis/visualizationPanel.tsx
@@ -1,21 +1,41 @@
-import React, { useState } from 'react';
-import { useAppDispatch, useGraphQueryResult, useQuerybuilderGraph, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
+import React, { useState, useRef, useEffect } from 'react';
+import {
+  useAppDispatch,
+  useGraphQueryResult,
+  useQuerybuilderGraph,
+  useSessionCache,
+  useVisualizationState,
+} from '@graphpolaris/shared/lib/data-access';
 import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 import { DropdownItem, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
 import ControlContainer from '@graphpolaris/shared/lib/components/controls';
 import { Button } from '@graphpolaris/shared/lib/components/buttons';
-import { VisualizationComponent } from '@graphpolaris/shared/lib/vis';
-import VisualizationDialog from './VisualizationDialog';
+
+import { VisualizationDialog } from './configuration';
 import { Settings as SettingsIcon, Apps as AppsIcon } from '@mui/icons-material';
+import { VisualizationManager, Visualizations } from './visualizationManager';
 
 export const VisualizationPanel = () => {
-  const vis = useVisualizationState();
   const graphQueryResult = useGraphQueryResult();
   const query = useQuerybuilderGraph();
   const dispatch = useAppDispatch();
+  const vis = useVisualizationState();
   const [visDropdownOpen, setVisDropdownOpen] = useState<boolean>(false);
   const [showVisSettings, setShowVisSettings] = useState<boolean>(false);
+  const visDropdownRef = useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+    const handleClickOutside = (event: MouseEvent) => {
+      if (visDropdownRef.current && !visDropdownRef.current.contains(event.target as Node)) {
+        setVisDropdownOpen(false);
+      }
+    };
+    if (visDropdownOpen) document.addEventListener('mousedown', handleClickOutside);
+    return () => {
+      document.removeEventListener('mousedown', handleClickOutside);
+    };
+  }, [visDropdownOpen]);
 
   return (
     <div className="vis-panel h-full w-full overflow-y-auto" style={graphQueryResult.nodes.length === 0 ? { overflow: 'hidden' } : {}}>
@@ -29,7 +49,8 @@ export const VisualizationPanel = () => {
             size="xs"
             iconComponent={<SettingsIcon />}
             onClick={() => {
-              setShowVisSettings(!showVisSettings);
+              // TODO
+              // setShowVisSettings(!showVisSettings);
             }}
           />
           <Button
@@ -42,18 +63,20 @@ export const VisualizationPanel = () => {
             }}
           />
           {visDropdownOpen && (
-            <DropdownItemContainer align="top-6 right-6">
-              {Object.keys(Visualizations).map((key) => (
-                <DropdownItem
-                  key={key}
-                  value={key}
-                  onClick={() => {
-                    setVisDropdownOpen(false);
-                    dispatch(setActiveVisualization(Visualizations[key as keyof typeof Visualizations]));
-                  }}
-                />
-              ))}
-            </DropdownItemContainer>
+            <div ref={visDropdownRef}>
+              <DropdownItemContainer align="top-6 right-6">
+                {Object.keys(Visualizations).map((key) => (
+                  <DropdownItem
+                    key={key}
+                    value={key}
+                    onClick={() => {
+                      setVisDropdownOpen(false);
+                      dispatch(setActiveVisualization(key));
+                    }}
+                  />
+                ))}
+              </DropdownItemContainer>
+            </div>
           )}
         </ControlContainer>
       </div>
@@ -69,7 +92,7 @@ export const VisualizationPanel = () => {
         </div>
       ) : (
         <div className="w-full h-full">
-          <VisualizationComponent />
+          <VisualizationManager />
         </div>
       )}
     </div>
diff --git a/libs/shared/lib/vis/visualizations/documentation.mdx b/libs/shared/lib/vis/visualizations/documentation.mdx
new file mode 100644
index 0000000000000000000000000000000000000000..74c0fd8ac0bd4aebfa4f4c873ebc55faf636adda
--- /dev/null
+++ b/libs/shared/lib/vis/visualizations/documentation.mdx
@@ -0,0 +1,89 @@
+import { Meta, Unstyled } from '@storybook/blocks';
+
+<Meta title="Visualizations/Implementation" />
+
+# Visualization documentation
+
+## Contents
+
+1. [Introduction](#Introduction)
+2. [Overview](#Overview)
+3. [Props](#Props)
+
+## Introduction
+
+This is the documentation for implementing visualizations within the GraphPolaris system.
+
+## Overview
+
+The basic code implementation looks like this:
+
+```jsx
+import React from 'react';
+import { VisualizationPropTypes, VISComponentType } from '@graphpolaris/shared/lib/vis/Types';
+
+/**
+ * Visualization Component
+ * @param {object} props - Props for the Visualization component.
+ * @param {GraphQueryResult} props.data - Queried data from the database.
+ * @param {object} props.schema - Database schema.
+ * @param {any} props.ml - Machine-learning results.
+ * @param {function} props.dispatch - Redux dispatch for state management.
+ * @param {object} props.globalConfig - Global configurations for the panel (applicable to all visualizations).
+ * @param {object} props.settings - Local configuration props unique to this visualization.
+ * @param {object} props.encodings - data attributes mapped to visual variables.
+ * @returns {JSX.Element} - Rendered content of the visualization.
+ */
+export const Visualization = ({ data, schema, ml, dispatch, globalConfig, settings, encodings }) => {
+  // Perform additional processing specific to this visualization using the provided props
+
+  return (
+    // Rendered visualization content
+  );
+};
+
+/**
+ * Visualization Component Object
+ * Contains information and configurations for the exported visualization component.
+ */
+export const VisualizationComponent: VISComponentType = {
+  displayName: 'Visualization',
+  VIS: Visualization,
+  settings: {},
+  encodings: {},
+};
+```
+
+### VisualizationComponent
+
+The visualization component is what gets exported and is used in the visualization manager. It must have a displayName and VIS.
+
+## Props
+
+### Data prop
+
+Contains the queried data from the database.
+
+### Schema prop
+
+Contains the database schema.
+
+### ML prop
+
+Contains the Machine-Learning results.
+
+### Dispatch prop
+
+Contains the dispatch for the redux state.
+
+### Global config prop
+
+Contains the global configurations for the panel that is the same for every visualization.
+
+### Settings prop
+
+Contains the local config props that are unique per visualization.
+
+### Encodings prop
+
+Contains the encodings for the visualization.
diff --git a/libs/shared/lib/vis/visualizations/index.tsx b/libs/shared/lib/vis/visualizations/index.tsx
index 2ba6e220ca80105be82e4bb2b7c333283cc62cab..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/libs/shared/lib/vis/visualizations/index.tsx
+++ b/libs/shared/lib/vis/visualizations/index.tsx
@@ -1,6 +0,0 @@
-export * from './rawjsonvis';
-export * from './nodelink';
-export * from './paohvis';
-export * from './semanticsubstrates';
-export * from './table_vis';
-export * from './matrix';
diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx
index 2d27524f0f2312efc60a0eeb9e22108dd18d4700..877464ba4d13debaae69f74cfe99c849c3b4d658 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx
@@ -112,7 +112,7 @@ export function MapPanel({ graph, layers, showFilter, setShowFilter }: Props) {
         onClick={handleSelect}
         onHover={({ object }) => setHoverObject(object !== undefined ? object : null)}
       />
-      <SelectedMenu selected={selected} />
+      {/* <SelectedMenu selected={selected} />
       <SecondaryMenu
         mapSize={mapSize}
         setMapSize={setMapSize}
@@ -120,7 +120,7 @@ export function MapPanel({ graph, layers, showFilter, setShowFilter }: Props) {
         flyToBoundingBox={flyToBoundingBox}
         setSelectingRectangle={setSelectingRectangle}
         selectingRectangle={selectingRectangle}
-      />
+      /> */}
       {showFilter && <FilterMenu graph={graph} setShowFilter={setShowFilter} />}
     </div>
   );
diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx
index 92ebf0d7e887db417ebb55799638f0e4642f49af..9f9138d21862a6fbb7191fc6beef0817c184c780 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import { Provider } from 'react-redux';
 import { configureStore } from '@reduxjs/toolkit';
 import {
@@ -11,7 +11,7 @@ import {
   visualizationSlice,
 } from '../../../data-access/store';
 import { mockMobilityQueryResult, bigMockQueryResults } from '../../../mock-data';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/MapVis',
@@ -45,7 +45,7 @@ export const DutchVehicleTheft = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: mockMobilityQueryResult } }));
-    // dispatch(setActiveVisualization(Visualizations.MapVis));
+    dispatch(setActiveVisualization('MapVis'));
   },
 };
 
@@ -53,7 +53,7 @@ export const AmericanFlights = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: bigMockQueryResults } }));
-    // dispatch(setActiveVisualization(Visualizations.MapVis));
+    dispatch(setActiveVisualization('MapVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
index 65dd5a40bb6f4d8b655dc409cc831ff6d74ea0e1..784bb118c393499748de510dcaa1eec86cb6f25f 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
@@ -2,13 +2,11 @@ import React from 'react';
 import { MapPanel, LayerPanel } from './components';
 import GraphModel from './graphModel';
 import { GraphType, Layer } from './Types';
-import { VISComponentType, VisualizationPropTypes, localConfigSchemaType } from '../../types';
+import { VISComponentType, VisualizationPropTypes } from '../../types';
 
 export type MapProps = {};
 
-const displayName = 'MapVis';
-
-export default function MapVis({ data, schema, localConfig }: VisualizationPropTypes<typeof displayName>) {
+export default function MapVis({ data, schema, settings }: VisualizationPropTypes) {
   const [layers, setLayers] = React.useState<Layer[]>([]);
   const [showFilter, setShowFilter] = React.useState<boolean>(false);
 
@@ -24,17 +22,15 @@ export default function MapVis({ data, schema, localConfig }: VisualizationPropT
   }
 
   return (
-    <div className="flex flex-row justify-between overflow-hidden w-screen h-screen font-sans">
+    <div className="flex flex-row justify-between overflow-hidden h-full w-full font-sans">
       <MapPanel graph={graph} layers={layers} showFilter={showFilter} setShowFilter={setShowFilter} />
-      <LayerPanel layers={layers} setLayers={setLayers} graphInfo={graph.getGraphInfo()} setShowFilter={setShowFilter} />
+      {/* <LayerPanel layers={layers} setLayers={setLayers} graphInfo={graph.getGraphInfo()} setShowFilter={setShowFilter} /> */}
     </div>
   );
 }
 
-const localConfigSchema: localConfigSchemaType = {};
-
-export const TableComponent: VISComponentType = {
-  displayName: displayName,
+export const MapComponent: VISComponentType = {
+  displayName: 'Map',
   VIS: MapVis,
-  localConfigSchema: localConfigSchema,
+  settings: {},
 };
diff --git a/libs/shared/lib/vis/visualizations/matrix/matrixvis.tsx b/libs/shared/lib/vis/visualizations/matrix/matrixvis.tsx
deleted file mode 100644
index 5bfd89dcc74f775b02257d020d62fa26b9164b17..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/visualizations/matrix/matrixvis.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React, { useEffect, useRef, useState } from 'react';
-import { useImmer } from 'use-immer';
-import { GraphQueryResult, useGraphQueryResult } from '../../../data-access/store';
-import { ML } from '../../../data-access/store/mlSlice';
-import { LinkType, NodeType } from './Types';
-import { MatrixPixi } from './components/MatrixPixi';
-import { VisualizationPropTypes, VISComponentType, localConfigSchemaType } from '../../types';
-
-interface Props {
-  loading?: boolean;
-  // currentColours: any;
-}
-
-// const displayName = 'Matrix Visualization';
-
-export const MatrixVis = React.memo(({ data, ml, dispatch, localConfig }: VisualizationPropTypes<typeof displayName>) => {
-  const ref = useRef<HTMLDivElement>(null);
-  const [graph, setGraph] = useImmer<GraphQueryResult | undefined>(undefined);
-  const [highlightNodes, setHighlightNodes] = useState<NodeType[]>([]);
-  const [highlightedLinks, setHighlightedLinks] = useState<LinkType[]>([]);
-
-  useEffect(() => {
-    if (data) {
-      setGraph(
-        data,
-        // parseQueryResult(graphQueryResult, ml, {
-        //   defaultX: (ref.current?.clientWidth || 1000) / 2,
-        //   defaultY: (ref.current?.clientHeight || 1000) / 2,
-        // })
-      );
-    }
-  }, [data, ml]);
-
-  return (
-    <>
-      <MatrixPixi graph={graph} highlightNodes={highlightNodes} highlightedLinks={highlightedLinks} localConfig={localConfig} />
-
-      {/* <VisConfigPanelComponent> */}
-      {/* <NodeLinkConfigPanelComponent
-               graph={this.state.graph}
-               nlViewModel={this.nodeLinkViewModel}
-            /> */}
-      {/*</VisConfigPanelComponent>*/}
-      {/*<VisConfigPanelComponent isLeft>*/}
-      {/*  <AttributesConfigPanel nodeLinkViewModel={this.nodeLinkViewModel} />*/}
-      {/* </VisConfigPanelComponent> */}
-    </>
-  );
-});
-
-const displayName = 'MatrixVis';
-const localConfigSchema: localConfigSchemaType = {
-  marks: {
-    type: 'dropdown',
-    options: ['rect', 'circle'],
-    value: 'rect',
-    label: 'Configure Marks',
-  },
-};
-
-export const MatrixVisComponent: VISComponentType = {
-  displayName: displayName,
-  VIS: MatrixVis,
-  localConfigSchema: localConfigSchema,
-};
-export default MatrixVisComponent;
diff --git a/libs/shared/lib/vis/visualizations/matrix/Types.tsx b/libs/shared/lib/vis/visualizations/matrixvis/Types.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/Types.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/Types.tsx
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/BitmapTextLabel.ts b/libs/shared/lib/vis/visualizations/matrixvis/components/BitmapTextLabel.ts
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/BitmapTextLabel.ts
rename to libs/shared/lib/vis/visualizations/matrixvis/components/BitmapTextLabel.ts
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/ColumnGraphicsComponent.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnGraphicsComponent.tsx
similarity index 77%
rename from libs/shared/lib/vis/visualizations/matrix/components/ColumnGraphicsComponent.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/ColumnGraphicsComponent.tsx
index facb7ff10e4b7f6f4d5ffb2c5dede333dfd32c1a..7226749f9bf6d3e1f53aee905d84226ac19adef5 100644
--- a/libs/shared/lib/vis/visualizations/matrix/components/ColumnGraphicsComponent.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnGraphicsComponent.tsx
@@ -50,14 +50,9 @@ export const createColumn = (
       gfx.drawRect(0, i * cellWidth, cellWidth, cellHeight);
       gfx.endFill();
     } else {
-      // console.log('inOutEdge', inOutEdge, Math.min(inOutEdge.length, 1), currentVisMapping.colorScale(Math.min(inOutEdge.length, 1)));
-
       if (outEdges.length > 0) {
         const thisEdge = outEdges[0];
-        // console.log('inOutEdge', thisEdge);
-
         const value = byString(thisEdge, currentVisMapping.attribute);
-        // console.log('value', value);
 
         color = currentVisMapping.colorScale(value);
       }
@@ -67,25 +62,8 @@ export const createColumn = (
       gfx.drawRect(0, i * cellWidth, cellWidth, cellHeight);
       gfx.endFill();
     }
-
-    // if (inOutEdge.length > 0) {
-    //   color = fgCellColor;
-    // } else {
-    //   color = bgCellColor;
-    // }
   }
 
-  //   gfx.on('pointerdown', onButtonDown);
-  // .on('pointerup', onDragEnd).on('pointerupoutside', onDragEnd);
-
-  // for (let i = 0; i < edges.length; i++) {
-  //   const edge = edges[i];
-  //   // draw an rectangle with a fill
-  //   gfx.beginFill(0xff0000, 0.5);
-  //   gfx.drawRect(0, i * cellWidth, cellWidth, cellHeight * nodes.length);
-  //   gfx.endFill();
-  // }
-
   return gfx;
 };
 
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/ColumnLabelTrack.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnLabelTrack.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/ColumnLabelTrack.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/ColumnLabelTrack.tsx
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/ColumnSpriteComponent.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnSpriteComponent.tsx
similarity index 70%
rename from libs/shared/lib/vis/visualizations/matrix/components/ColumnSpriteComponent.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/ColumnSpriteComponent.tsx
index b04470376494f3a323e6c833ef6927c88773280a..6176f1d5b9e6859980f94dfbcaa4d49735338cfa 100644
--- a/libs/shared/lib/vis/visualizations/matrix/components/ColumnSpriteComponent.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnSpriteComponent.tsx
@@ -11,7 +11,6 @@ export const createColumn = (
   cellWidth: number,
   cellHeight: number
 ) => {
-  // console.log('createColumn', id, nodes.length, edges.length);
   const sprite = new Sprite(Texture.WHITE);
   sprite.name = 'col_' + id;
   sprite.eventMode = 'static';
@@ -20,8 +19,6 @@ export const createColumn = (
   const edgesForThisColumn = edges.filter((edge) => {
     return edge.from === nodesCol[id].id || edge.to === nodesCol[id].id;
   });
-  // console.log('edgesForThisColumn', nodesCol[id].id, edgesForThisColumn);
-  // const color = new Color({ r: 255 * (id / nodesCol.length), g: 255, b: 255, a: 1 });
   const bgCellColor = dataColors.neutral['5'];
   const fgCellColor = tailwindColors.entity.DEFAULT;
   let color = bgCellColor;
@@ -47,14 +44,5 @@ export const createColumn = (
     // sprite.drawRect(0, i * cellWidth, cellWidth, cellHeight);
     // sprite.endFill();
   }
-  //   sprite.on('pointerdown', onButtonDown);
-  // .on('pointerup', onDragEnd).on('pointerupoutside', onDragEnd);
-  // for (let i = 0; i < edges.length; i++) {
-  //   const edge = edges[i];
-  //   // draw an rectangle with a fill
-  //   gfx.beginFill(0xff0000, 0.5);
-  //   gfx.drawRect(0, i * cellWidth, cellWidth, cellHeight * nodes.length);
-  //   gfx.endFill();
-  // }
   return sprite;
 };
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/MatrixExport.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixExport.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/MatrixExport.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/MatrixExport.tsx
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/MatrixPixi.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx
similarity index 64%
rename from libs/shared/lib/vis/visualizations/matrix/components/MatrixPixi.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx
index 4ed2df8868f9bcf76f87614567b70feedf1ddf1d..5c48d1f27cb154a8d509201958a5e111a620d11d 100644
--- a/libs/shared/lib/vis/visualizations/matrix/components/MatrixPixi.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx
@@ -1,5 +1,4 @@
 import { Edge, GraphQueryResult, Node, useML, useSearchResultData } from '@graphpolaris/shared/lib/data-access';
-import { Cull } from '@pixi-essentials/cull';
 import { dataColors, tailwindColors } from 'config';
 import * as d3 from 'd3';
 import { Viewport } from 'pixi-viewport';
@@ -23,10 +22,9 @@ import { NLPopup } from './MatrixPopup';
 import { Actions, Interpolations } from 'pixi-actions';
 
 import Color from 'color';
-import { localConfigSchemaType } from '../../../types';
+import { SettingTypes } from '../../../configuration/settings';
 import { createColumn } from './ColumnGraphicsComponent';
 import { ReorderingManager } from './ReorderingManager';
-import { TextLabel } from './TextLabel';
 
 type Props = {
   // onClick: (node: NodeType, pos: IPointData) => void;
@@ -36,45 +34,34 @@ type Props = {
   currentShortestPathEdges?: LinkType[];
   highlightedLinks?: LinkType[];
   graph?: GraphQueryResult;
-  localConfig: localConfigSchemaType;
+  localConfig: SettingTypes;
 };
 
 const app = new Application({ background: 0xffffff, antialias: true, autoDensity: true, eventMode: 'auto' });
+const columnsContainer = new Container();
+const columnAxisContainer = new Container();
+const rowsContainer = new Container();
+
 //////////////////
 // MAIN COMPONENT
 //////////////////
-export const MatrixPixi = (props: Props) => {
-  const columnLayer = new Container();
-  const columnLabelLayer = new Container();
-  const rowsLabelLayer = new Container();
-
-  let lastZoom = Infinity;
 
+export const MatrixPixi = (props: Props) => {
   let config = {
     textOffsetX: 100,
     textOffsetY: 100,
     visMapping: [] as any[], // TODO type
 
-    CELL_HEIGHT: 50,
-    CELL_WIDTH: 50,
-
-    LABEL_FONT_FAMILY: 'Roboto',
-    LABEL_FILL: 0x000000,
-    LABEL_ALIGN: 'center',
-    LABEL_FONT_SIZE: 25,
-    LABEL_MARGIN: 4,
-    LABEL_MIN_WIDTH: 6,
-
-    ANIMATION_DURATION: 0.8,
+    cellHeight: 100,
+    cellWidth: 100,
   };
 
   let columnOrder: string[] = [];
   let rowOrder: string[] = [];
-  const [columnOrderState, setColumnOrderState] = useState<string[]>([]);
-  const [rowOrderState, setRowOrderState] = useState<string[]>([]);
 
   const [quickPopup, setQuickPopup] = useState<{ node: NodeType; pos: IPointData } | undefined>();
   const [popups, setPopups] = useState<{ node: NodeType; pos: IPointData }[]>([]);
+  // const [columnOrder, setColumnOrder] = useState<string[]>([]);
 
   const nodeMap = useRef(new Map<string, Graphics>());
   const linkMap = useRef(new Map<string, Graphics>());
@@ -84,8 +71,7 @@ export const MatrixPixi = (props: Props) => {
   const ml = useML();
   const searchResults = useSearchResultData();
 
-  const cull = new Cull();
-  let cullDirty = useRef(true);
+  // const imperative = useRef<any>(null);
 
   function resize() {
     const width = ref?.current?.clientWidth || 1000;
@@ -121,11 +107,8 @@ export const MatrixPixi = (props: Props) => {
     }
   }, [ref]);
 
-  // useEffect(() => {
-  //   console.log('newColumnOrder', columnOrder);
-  // }, [columnOrder]);
-
   useEffect(() => {
+    console.log('graph change');
     // console.log('graph changed', props.graph, ref.current, ref.current.children.length > 0, imperative.current);
     if (props.graph && ref.current && ref.current.children.length > 0) {
       if (!isSetup.current) setup();
@@ -134,6 +117,7 @@ export const MatrixPixi = (props: Props) => {
   }, [props.graph]);
 
   useEffect(() => {
+    console.log('config change');
     // console.log('graph changed', props.graph, ref.current, ref.current.children.length > 0, imperative.current);
     if (props.graph && ref.current && ref.current.children.length > 0) {
       setup();
@@ -161,7 +145,7 @@ export const MatrixPixi = (props: Props) => {
 
   function onButtonDown(event: FederatedPointerEvent) {
     console.log(
-      event.currentTarget,
+      event.currentTarget
       // graph.nodes.find((node) => node.id === event.currentTarget.name)
     );
   }
@@ -174,45 +158,45 @@ export const MatrixPixi = (props: Props) => {
       if (forceClear) {
         nodeMap.current.clear();
         linkMap.current.clear();
-        rowsLabelLayer.removeChildren();
-        columnLayer.removeChildren();
+        rowsContainer.removeChildren();
+        columnsContainer.removeChildren();
       }
     }
   };
 
   const onHoverColumn = (event: any, currentNode: Node | null) => {
-    app.renderer.events.cursorStyles.pointer = 'crosshair';
+    app.renderer.plugins.interaction.cursorStyles.pointer = 'crosshair';
     if (!currentNode) {
-      rowsLabelLayer.children.forEach((row) => {
+      rowsContainer.children.forEach((row) => {
         row.alpha = 1;
       });
-      columnLayer.children.forEach((col) => {
+      columnsContainer.children.forEach((col) => {
         col.alpha = 1;
       });
-      columnLabelLayer.children.forEach((colText) => {
+      columnAxisContainer.children.forEach((colText) => {
         colText.alpha = 1;
       });
 
       return;
     }
 
-    columnLayer.children.forEach((col) => {
+    columnsContainer.children.forEach((col) => {
       col.alpha = 0.25;
     });
     event.currentTarget.alpha = 1;
 
-    columnLabelLayer.children.forEach((colText) => {
+    columnAxisContainer.children.forEach((colText) => {
       colText.alpha = 0.25;
       if (colText.name?.endsWith(currentNode.id)) colText.alpha = 1;
     });
 
     if (!props.graph) return;
-    rowsLabelLayer.children.forEach((col) => {
+    rowsContainer.children.forEach((col) => {
       col.alpha = 0.25;
     });
 
     if (!props.graph) return;
-    rowsLabelLayer.children.forEach((col) => {
+    rowsContainer.children.forEach((col) => {
       col.alpha = 0.25;
     });
 
@@ -222,17 +206,17 @@ export const MatrixPixi = (props: Props) => {
     });
 
     edgesForThisColumn.forEach((edge) => {
-      let row = rowsLabelLayer.getChildByName(edge.to);
+      let row = rowsContainer.getChildByName(edge.to);
       if (row) row.alpha = 1;
 
-      row = rowsLabelLayer.getChildByName(edge.from);
+      row = rowsContainer.getChildByName(edge.from);
       if (row) row.alpha = 1;
     });
   };
 
   const reorderColumns = (columnOrder: string[]) => {
-    const columns = columnLayer.children;
-    const columnLabels = columnLabelLayer.children;
+    const columns = columnsContainer.children;
+    const columnsText = columnAxisContainer.children;
     let columnPositions: Point[] = [];
     for (let i = 0; i < columns.length; i++) {
       const col = columns[i];
@@ -240,14 +224,14 @@ export const MatrixPixi = (props: Props) => {
     }
 
     let columnTextPositions: Point[] = [];
-    for (let i = 0; i < columnLabels.length; i++) {
-      const col = columnLabels[i];
+    for (let i = 0; i < columnsText.length; i++) {
+      const col = columnsText[i];
       columnTextPositions.push(new Point(col.position.x, col.position.y));
     }
 
     if (columnTextPositions.length !== columnPositions.length)
       throw new Error(
-        'columnTextPositions and columnPositions have different length ' + columnTextPositions.length + ' / ' + columnPositions.length,
+        'columnTextPositions and columnPositions have different length ' + columnTextPositions.length + ' / ' + columnPositions.length
       );
 
     for (let i = 0; i < columns.length; i++) {
@@ -262,7 +246,7 @@ export const MatrixPixi = (props: Props) => {
 
       const newPositionText = columnTextPositions[index];
       // move column to new position
-      Actions.moveTo(columnLabels[i], newPositionText.x, newPositionText.y, 1, Interpolations.pow2out).play();
+      Actions.moveTo(columnsText[i], newPositionText.x, newPositionText.y, 1, Interpolations.pow2out).play();
     }
   };
 
@@ -273,9 +257,9 @@ export const MatrixPixi = (props: Props) => {
     rowOrder: string[],
     colorScale: any,
     cellWidth: number,
-    cellHeight: number,
+    cellHeight: number
   ) => {
-    const rows = rowsLabelLayer.children;
+    const rows = rowsContainer.children;
     let rowPositions: Point[] = [];
     for (let i = 0; i < rows.length; i++) {
       const row = rows[i];
@@ -293,44 +277,40 @@ export const MatrixPixi = (props: Props) => {
       Actions.moveTo(row, newPosition.x, newPosition.y, 1, Interpolations.pow2out).play();
     }
 
-    const colOrder = columnLayer.children.map((col) => col.name) as string[];
+    const colOrder = columnsContainer.children.map((col) => col.name) as string[];
     console.log('colOrder', colOrder);
     if (!colOrder) throw new Error('colOrder is undefined');
-    columnLayer.removeChildren();
+    columnsContainer.removeChildren();
     setupColumns(edges, colOrder, rowOrder);
   };
 
   const setup = () => {
     if (!props.graph) throw Error('Graph is undefined; setup not possible');
-    console.log('setup matrixvis with graph:', props.graph);
-
-    columnLayer.removeChildren();
-    rowsLabelLayer.removeChildren();
-    columnLabelLayer.removeChildren();
-    viewport.current?.removeAllListeners();
-    viewport.current?.removeChildren();
+    columnsContainer.removeChildren();
+    rowsContainer.removeChildren();
+    columnAxisContainer.removeChildren();
     app.stage.removeChildren();
 
     const size = ref.current?.getBoundingClientRect();
     viewport.current = new Viewport({
       screenWidth: size?.width || 1000,
       screenHeight: size?.height || 1000,
+      worldWidth: size?.width || 1000,
+      worldHeight: size?.height || 1000,
       stopPropagation: true,
       events: app.renderer.events, // the interaction module is important for wheel to work properly when renderer.view is placed or scaled
     });
 
-    // setupZoomLogic();
+    app.stage.addChild(viewport.current);
 
-    viewport.current.addChild(columnLayer);
+    viewport.current.addChild(columnsContainer);
+    viewport.current.addChild(rowsContainer);
 
-    const groupByType = props.graph.nodes.reduce(
-      (group: any, node: Node) => {
-        if (!group[node.label]) group[node.label] = [];
-        group[node.label].push(node);
-        return group;
-      },
-      {} as { [key: string]: Node[] },
-    );
+    const groupByType = props.graph.nodes.reduce((group: any, node: Node) => {
+      if (!group[node.label]) group[node.label] = [];
+      group[node.label].push(node);
+      return group;
+    }, {} as { [key: string]: Node[] });
 
     // order groupByType by size
     const ordered = Object.entries(groupByType).sort((a: any[], b: any[]) => b[1].length - a[1].length);
@@ -365,127 +345,31 @@ export const MatrixPixi = (props: Props) => {
     columnOrder = newOrdering.columnOrder;
     rowOrder = newOrdering.rowOrder;
 
+    config.cellWidth = Math.max((size?.width || 1000) / props.graph.nodes.length, (size?.height || 1000) / props.graph.nodes.length);
+    config.cellHeight = config.cellWidth;
+
+    // console.log('currentColumnOrder', columnOrder);
+
     setupVisualizationEncodingMapping(props.localConfig);
 
     setupColumns(props.graph.edges, columnOrder, rowOrder);
-    setupColumnLabels(columnOrder);
+    setupColumnLegend(columnOrder);
     setupColumnInteractivity(cols);
 
-    setupRowLabels(rows, rowOrder);
+    setupRowLegend(rows, rowOrder);
     setupRowInteractivity(cols, rows, rowOrder, d3.scaleOrdinal(d3.schemeCategory10));
 
+    console.log('setup matrixvis with graph:', props.graph);
+
     // activate plugins
     viewport.current.drag().pinch().wheel().animate({}).decelerate({ friction: 0.75 });
-    lastZoom = viewport.current.scale.x;
 
-    app.stage.addChild(viewport.current);
     app.ticker.add(tick);
-
-    // requestRender();
     update();
     isSetup.current = true;
-
-    cull.clear();
-    cull.addAll(viewport.current.children);
-    cull.addAll(rowsLabelLayer.children);
-    cull.addAll(columnLabelLayer.children);
-    cull.addAll(columnLayer.children);
-    cullDirty.current = true;
-
-    setColumnOrderState([...columnOrder]);
-    setRowOrderState([...rowOrder]);
-
-    resetViewport();
   };
 
-  // let renderRequestId: number | undefined = undefined;
-  // const requestRender = () => {
-  //   if (renderRequestId) {
-  //     return;
-  //   }
-  //   renderRequestId = window.requestAnimationFrame(() => {
-  //     app.render();
-  //     renderRequestId = undefined;
-  //   });
-  // };
-
-  const updateVisibility = () => {
-    if (!viewport.current) return;
-
-    // culling
-    if (viewport.current.dirty || cullDirty.current) {
-      cull.cull(app.renderer.screen);
-      // console.log(
-      //   'culling',
-      //   [...viewport.current.children].filter((x) => x.visible === true).length,
-      //   [...viewport.current.children].filter((x) => x.visible === false).length,
-      //   [...rowsLabelLayer.children].filter((x) => x.visible === true).length,
-      //   [...rowsLabelLayer.children].filter((x) => x.visible === false).length,
-      //   [...columnLabelLayer.children].filter((x) => x.visible === true).length,
-      //   [...columnLabelLayer.children].filter((x) => x.visible === false).length,
-      //   [...columnLayer.children].filter((x) => x.visible === true).length,
-      //   [...columnLayer.children].filter((x) => x.visible === false).length
-      // );
-
-      viewport.current.dirty = false;
-      cullDirty.current = false;
-    }
-
-    // levels of detail
-    // if (viewport.current?.scale.x !== lastZoom) {
-    const zoom = viewport.current?.scale.x || 0.0;
-
-    const oneColumn = columnLayer.children[0];
-    if (!oneColumn) return;
-
-    const targetLabelWidth = oneColumn.getBounds().width;
-    const targetLabelHeight = targetLabelWidth; //cells have same width and height
-
-    [...columnLabelLayer.children]
-      .filter((x) => x.visible === true)
-      .forEach((columnItem) => {
-        if (targetLabelWidth < config.LABEL_MIN_WIDTH) {
-          columnItem.visible = false;
-        } else {
-          const textLabel = columnItem as TextLabel;
-          textLabel.visible = true;
-        }
-      });
-
-    [...rowsLabelLayer.children]
-      .filter((x) => x.visible === true)
-      .forEach((rowItem) => {
-        if (targetLabelWidth < config.LABEL_MIN_WIDTH) {
-          rowItem.visible = false;
-        } else {
-          rowItem.visible = true;
-        }
-      });
-
-    lastZoom = zoom;
-    // }
-  };
-
-  // TODO
-  // const setupZoomLogic = () => {
-  //   const zoomIn = () => {
-  //     if (!viewport.current) return
-  //     viewport.current.zoom(-WORLD_WIDTH / 10, true);
-  //   };
-  //   const zoomOut = () => {
-  //     if (!viewport.current) return;
-  //     viewport.current.zoom(WORLD_WIDTH / 10, true);
-  //   };
-  const resetViewport = () => {
-    if (!viewport.current) return;
-    viewport.current.center = new Point(viewport.current.worldWidth / 2, viewport.current.worldHeight / 2);
-
-    // console.log('resetViewPort', viewport.current.center);
-    viewport.current.fitWorld(true);
-  };
-  // }
-
-  const setupVisualizationEncodingMapping = (localConfig: localConfigSchemaType) => {
+  const setupVisualizationEncodingMapping = (localConfig: SettingTypes) => {
     if (!props.graph) throw new Error('Graph is undefined; cannot setup matrix');
     const visMapping = []; // TODO type
 
@@ -507,10 +391,10 @@ export const MatrixPixi = (props: Props) => {
 
         gfxContext.lineStyle(2, 0xffffff);
         if (this.encoding === 'rect') {
-          gfxContext.drawRect(0, i * config.CELL_HEIGHT, config.CELL_WIDTH, config.CELL_HEIGHT);
+          gfxContext.drawRect(0, i * config.cellHeight, config.cellWidth, config.cellHeight);
         }
         if (this.encoding === 'circle') {
-          gfxContext.drawCircle(config.CELL_WIDTH / 2, i * config.CELL_HEIGHT + config.CELL_HEIGHT / 2, config.CELL_WIDTH / 2);
+          gfxContext.drawCircle(config.cellWidth / 2, i * config.cellHeight + config.cellHeight / 2, config.cellWidth / 2);
         }
         gfxContext.endFill();
 
@@ -558,28 +442,20 @@ export const MatrixPixi = (props: Props) => {
       });
       // console.log('edgesForThisColumn', oneColumn.name, edgesForThisColumn);
 
-      const col = createColumn(rowOrder, edgesForThisColumn, config.visMapping, config.CELL_WIDTH, config.CELL_HEIGHT);
+      const col = createColumn(rowOrder, edgesForThisColumn, config.visMapping, config.cellWidth, config.cellHeight);
       oneColumn.addChild(col);
-      oneColumn.position.set(j * config.CELL_WIDTH + config.textOffsetX, config.textOffsetY);
-
-      columnLayer.addChild(oneColumn);
-
-      if (columnOrder.length < 100) {
-        oneColumn.alpha = 0;
-        Actions.sequence(
-          Actions.delay(j * 0.005 * Math.random()),
-          Actions.fadeIn(oneColumn, config.ANIMATION_DURATION, Interpolations.pow2out),
-        ).play();
-      } else {
-        oneColumn.alpha = 1;
-      }
+      oneColumn.position.set(j * config.cellWidth + config.textOffsetX, config.textOffsetY);
+
+      columnsContainer.addChild(oneColumn);
+      oneColumn.alpha = 0;
+      Actions.sequence(Actions.delay(j * 0.005 * Math.random()), Actions.fadeIn(oneColumn, 0.8, Interpolations.pow2out)).play();
     }
   }
 
   const setupColumnInteractivity = (cols: Node[]) => {
-    for (let j = 0; j < columnLayer.children.length; j++) {
-      const oneColumn = columnLayer.children[j];
-      oneColumn.eventMode = 'dynamic';
+    for (let j = 0; j < columnsContainer.children.length; j++) {
+      const oneColumn = columnsContainer.children[j];
+      oneColumn.interactive = true;
       oneColumn.on('pointerdown', onButtonDown);
       oneColumn.on('pointerover', (event) => {
         const col = cols.find((col) => col.id === oneColumn.name);
@@ -591,28 +467,34 @@ export const MatrixPixi = (props: Props) => {
     }
   };
 
-  const setupColumnLabels = (columnOrder: string[]) => {
-    columnLabelLayer.removeChildren();
-
-    columnLabelLayer.position.set(config.textOffsetX, 0);
+  const setupColumnLegend = (columnOrder: string[]) => {
+    // console.log('setupColumnLegend');
+    // columnAxisContainer.removeChildren();
+    columnAxisContainer.position.set(config.textOffsetX, 0);
     for (let j = 0; j < columnOrder.length; j++) {
-      const textLabel = new TextLabel(columnOrder[j], config);
-      // const textLabel = new BitmapTextLabel(columnOrder[j]);
-
-      // console.log('font resolved', columnOrder[j]);
-      textLabel.position.set(j * config.CELL_WIDTH + config.CELL_WIDTH / 4, config.textOffsetY - 5);
-      columnLabelLayer.addChild(textLabel);
+      // from the PixiJS documention: Setting a text object's scale to > 1.0 will result in blurry/pixely display,
+      // because the text is not re-rendered at the higher resolution needed to look sharp -
+      // it's still the same resolution it was when generated. To deal with this, you can render at a higher
+      // initial size and down-scale, instead.
+      const finalTextSize = 10;
+      const bigRandomScaleFactor = 10;
+      const basicText = new Text(columnOrder[j], { fontSize: finalTextSize * bigRandomScaleFactor, fill: 0x000000, align: 'center' });
+      basicText.position.set(j * config.cellWidth + config.cellWidth / 4, config.textOffsetY - 5);
+      basicText.scale.set(finalTextSize / (bigRandomScaleFactor * bigRandomScaleFactor));
+      basicText.rotation = (Math.PI * 3) / 2;
+      basicText.eventMode = 'none';
+      basicText.name = 'Text_' + columnOrder[j];
+      columnAxisContainer.addChild(basicText);
     }
-    viewport.current?.addChild(columnLabelLayer);
+    viewport.current?.addChild(columnAxisContainer);
 
     const columnAxis = new Sprite(Texture.WHITE);
-    columnAxis.width = columnOrder.length * config.CELL_WIDTH;
+    columnAxis.width = columnOrder.length * config.cellWidth;
     columnAxis.height = config.textOffsetY;
     columnAxis.tint = 0xaaaaaa;
     columnAxis.position.set(config.textOffsetX, 0);
     columnAxis.alpha = 0.0;
-    // columnAxis.interactive = true;
-    columnAxis.eventMode = 'dynamic';
+    columnAxis.interactive = true;
     columnAxis.on('pointerdown', (event) => {
       // const reordering = new ReorderColumnsAction(columnOrder);
 
@@ -626,10 +508,10 @@ export const MatrixPixi = (props: Props) => {
     });
     // columnAxis.on('pointerdown', reorderColumns);
     columnAxis.on('pointerover', (event) => {
-      Actions.fadeTo(columnAxis, 0.5, config.ANIMATION_DURATION, Interpolations.pow2out).play();
+      Actions.fadeTo(columnAxis, 0.5, 0.5, Interpolations.pow2out).play();
     });
     columnAxis.on('pointerout', (event) => {
-      Actions.fadeTo(columnAxis, 0.0, config.ANIMATION_DURATION, Interpolations.pow2out).play();
+      Actions.fadeTo(columnAxis, 0.0, 0.5, Interpolations.pow2out).play();
     });
     viewport.current?.addChild(columnAxis);
   };
@@ -653,46 +535,35 @@ export const MatrixPixi = (props: Props) => {
     return array;
   }
 
-  function setupRowLabels(rows: Node[], rowOrder: string[]) {
-    rowsLabelLayer.removeChildren();
-
-    // columnLabelLayer.position.set(config.textOffsetX, 0);
+  function setupRowLegend(rows: Node[], rowOrder: string[]) {
     for (let i = 0; i < rowOrder.length; i++) {
       const rowID = rowOrder[i];
-      const textLabel = new TextLabel(rowID, config);
-      textLabel.anchor.x = 1;
-      textLabel.anchor.y = 0;
-      textLabel.rotation = 0;
-      textLabel.name = rowID;
-      textLabel.position.set(config.textOffsetX - 5, i * config.CELL_HEIGHT + config.textOffsetY + config.CELL_HEIGHT / 4);
-      rowsLabelLayer.addChild(textLabel);
-
-      if (rowOrder.length < 100) {
-        textLabel.alpha = 0;
-        Actions.sequence(
-          Actions.delay(i * 0.005 * Math.random()),
-          Actions.fadeIn(textLabel, config.ANIMATION_DURATION, Interpolations.pow2out),
-        ).play();
-      } else {
-        textLabel.alpha = 1;
-      }
-    }
+      const finalTextSize = 10;
+      const bigRandomScaleFactor = 10;
+      const basicText = new Text(rowID, { fontSize: finalTextSize * bigRandomScaleFactor, fill: 0x000000, align: 'right' });
+      basicText.anchor.x = 1;
+      basicText.anchor.y = 0;
+      basicText.scale.set(finalTextSize / (bigRandomScaleFactor * bigRandomScaleFactor));
+      basicText.position.set(config.textOffsetX - 5, i * config.cellHeight + config.textOffsetY + config.cellHeight / 4);
+      basicText.name = rowID;
+      rowsContainer.addChild(basicText);
 
-    viewport.current?.addChild(rowsLabelLayer);
+      Actions.sequence(Actions.delay(i * 0.005 * Math.random()), Actions.fadeIn(basicText, 0.5, Interpolations.pow2out)).play();
+    }
   }
 
   const setupRowInteractivity = (cols: Node[], rows: Node[], rowOrder: string[], colorScale: any) => {
     const rowAxis = new Sprite(Texture.WHITE);
     rowAxis.width = config.textOffsetX;
-    rowAxis.height = rows.length * config.CELL_HEIGHT;
+    rowAxis.height = rows.length * config.cellHeight;
     rowAxis.tint = 0xaaaaaa;
     rowAxis.position.set(0, config.textOffsetY);
     rowAxis.alpha = 0.0;
-    rowAxis.eventMode = 'dynamic';
+    rowAxis.interactive = true;
     rowAxis.on('pointerdown', (event) => {
       if (props.graph) {
         rowOrder = shuffle(rowOrder);
-        reorderRows(cols, rows, props?.graph.edges, rowOrder, colorScale, config.CELL_WIDTH, config.CELL_HEIGHT);
+        reorderRows(cols, rows, props?.graph.edges, rowOrder, colorScale, config.cellWidth, config.cellHeight);
       }
     });
     rowAxis.on('pointerover', (event) => {
@@ -707,8 +578,6 @@ export const MatrixPixi = (props: Props) => {
   const tick = (delta: number) => {
     if (props.graph) {
       Actions.tick(delta / 60);
-
-      updateVisibility();
     }
   };
 
@@ -718,8 +587,6 @@ export const MatrixPixi = (props: Props) => {
         <NLPopup onClose={() => {}} data={popup} key={popup.node.id} />
       ))}
       {quickPopup && <NLPopup onClose={() => {}} data={quickPopup} />}
-
-      {/* {columnOrderState.length > 0 && <ColumnLabelTrack columnLabels={columnOrderState} config={config} />} */}
       <div className="h-full w-full overflow-hidden" ref={ref}></div>
     </>
   );
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/MatrixPopup.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPopup.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/MatrixPopup.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPopup.tsx
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/ReorderingManager.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/ReorderingManager.tsx
similarity index 76%
rename from libs/shared/lib/vis/visualizations/matrix/components/ReorderingManager.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/ReorderingManager.tsx
index a843663d38acb3dd118833232ccbca5ddf40f6ea..ae69d44e285be9e15fd93bb3cdaf49e9dd4f5398 100644
--- a/libs/shared/lib/vis/visualizations/matrix/components/ReorderingManager.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/components/ReorderingManager.tsx
@@ -10,27 +10,6 @@ export class ReorderingManager {
     this.graph = graph;
   }
 
-  //   // Precompute the orders.
-  //   orders = {
-  //     //
-  //     //   name: d3.range(n).sort(function (a, b) {
-  //     //     return d3.ascending(nodes[a].name, nodes[b].name);
-  //     //   }),
-  //     //   count: d3.range(n).sort(function (a, b) {
-  //     //     return nodes[b].count - nodes[a].count;
-  //     //   }),
-  //     //   group: d3.range(n).sort(function (a, b) {
-  //     //     var x = nodes[b].group - nodes[a].group;
-  //     //     return x != 0 ? x : d3.ascending(nodes[a].name, nodes[b].name);
-  //     //   }),
-  //     leafOrder: computeLeaforder,
-  //     //   leafOrderDist: computeLeaforderDist,
-  //     //   barycenter: computeBarycenter,
-  //     //   rcm: computeRCM,
-  //     //   spectral: computeSpectral,
-  //     //   nn2opt: computeNN2OPT,
-  //   };
-
   private computeNameOrder = (columnOrder: string[], rowOrder: string[]) => {
     const columnOrderSorted = columnOrder.sort((a, b) => a.localeCompare(b));
     const rowOrderSorted = rowOrder.sort((a, b) => a.localeCompare(b));
@@ -101,8 +80,6 @@ export class ReorderingManager {
       adjacency.push(rowOrderIDs);
     }
 
-    // console.log('adjacency', adjacency);
-
     return adjacency;
   }
 
@@ -137,9 +114,6 @@ export class ReorderingManager {
     );
 
     const nodes = columnOrder.map((id) => nodesTemp.find((node) => node.id === id));
-    const nodesWithRows = nodes.push(...rowOrder.map((id) => nodesTemp.find((node) => node.id === id)));
-    console.log('nodesTemp', nodes);
-
     const graphReorderJs = reorderAny.graph().nodes(nodes);
 
     const edges = graph.edges.map((edge) => {
@@ -156,7 +130,6 @@ export class ReorderingManager {
       } as any;
     });
 
-    // const edges = rowOrder.map((id) => edgesTemp.find((edge) => edge.id === id));
     graphReorderJs.links(edges);
     graphReorderJs.init();
 
@@ -181,14 +154,6 @@ export class ReorderingManager {
     const reorderGraph = this.getReorderJSGraph(this.graph, columnOrder, rowOrder);
 
     var order = reorderAny.spectral_order(reorderGraph);
-    // rcm.forEach(function (lo, i) {
-    //   nodes[i].rcm = lo;
-    // });
-
-    // return nodes.map(function (n) {
-    //   return n.rcm;
-    // });
-
     const columnOrderSorted = order.map((i: number) => columnOrder[i]).filter((a: string) => columnOrder.includes(a));
     const rowOrderSorted = order.map((i: number) => rowOrder[i]).filter((a: string) => rowOrder.includes(a));
 
@@ -202,13 +167,6 @@ export class ReorderingManager {
     const reorderGraph = this.getReorderJSGraph(this.graph, columnOrder, rowOrder);
 
     var order = reorderAny.bfs_order(reorderGraph);
-    // rcm.forEach(function (lo, i) {
-    //   nodes[i].rcm = lo;
-    // });
-
-    // return nodes.map(function (n) {
-    //   return n.rcm;
-    // });
 
     const columnOrderSorted = order.map((i: number) => columnOrder[i]).filter((a: string) => columnOrder.includes(a));
     const rowOrderSorted = order.map((i: number) => rowOrder[i]).filter((a: string) => rowOrder.includes(a));
@@ -225,7 +183,6 @@ export class ReorderingManager {
 
     const order = reorderAny.pca_order(columnOrderUnSorted);
     const columnOrderSorted = order.map((i: number) => columnOrder[i]);
-    // const rowOrderSorted = order.map((i: number) => rowOrder[i]).filter((a: string) => rowOrder.includes(a));
 
     console.debug('improved pca', order, columnOrderSorted, rowOrder);
 
@@ -236,22 +193,22 @@ export class ReorderingManager {
     console.log('reorderMatrix', orderingname);
     switch (orderingname.toLowerCase()) {
       case 'leafordering': {
-        return this.computeLeaforder(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeLeaforder(columnOrder, rowOrder);
       }
       case 'name': {
-        return this.computeNameOrder(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeNameOrder(columnOrder, rowOrder);
       }
       case 'count': {
-        return this.computeCountOrder(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeCountOrder(columnOrder, rowOrder);
       }
       case 'barycenter': {
-        return this.computeBarycenter(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeBarycenter(columnOrder, rowOrder);
       }
       case 'rcm': {
-        return this.computeRCM(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeRCM(columnOrder, rowOrder);
       }
       case 'spectral': {
-        return this.computeSpectal(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeSpectal(columnOrder, rowOrder);
       }
       // case 'pca': {
       //   return this.computePCA(columnOrder, rowOrder);
@@ -260,10 +217,10 @@ export class ReorderingManager {
       //   return this.computeBFS(columnOrder, rowOrder);
       // }
       case 'none' || 'identity': {
-        return { columnOrder, rowOrder } as { columnOrder: string[]; rowOrder: string[] };
+        return { columnOrder, rowOrder };
       }
       default: {
-        return this.computeLeaforder(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeLeaforder(columnOrder, rowOrder);
       }
     }
   };
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/TextLabel.ts b/libs/shared/lib/vis/visualizations/matrixvis/components/TextLabel.ts
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/TextLabel.ts
rename to libs/shared/lib/vis/visualizations/matrixvis/components/TextLabel.ts
diff --git a/libs/shared/lib/vis/visualizations/matrix/index.ts b/libs/shared/lib/vis/visualizations/matrixvis/index.ts
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/index.ts
rename to libs/shared/lib/vis/visualizations/matrixvis/index.ts
diff --git a/libs/shared/lib/vis/visualizations/matrix/matrix.stories.tsx b/libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
similarity index 61%
rename from libs/shared/lib/vis/visualizations/matrix/matrix.stories.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
index 766845b3ce986e7a81d00c0c8ccf456ba0e68842..69dca085516b93572cfa9a2aa7c72c70c498113a 100644
--- a/libs/shared/lib/vis/visualizations/matrix/matrix.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
@@ -2,26 +2,9 @@ import { Meta } from '@storybook/react';
 
 import { configureStore } from '@reduxjs/toolkit';
 import { Provider } from 'react-redux';
-import {
-  big2ndChamberQueryResult,
-  bigMockQueryResults,
-  big2ndChamberSchemaRaw,
-  smallFlightsQueryResults,
-  mockLargeQueryResults,
-  recommendationPersonActedInMovieQueryResult,
-  recommendationPersonActedInMovieQueryResultPayload,
-  slackReactionToThreadedMessageQueryResultPayload,
-} from '../../../mock-data';
-
-// export * from './mockLargeQueryResults';
-// export * from './big2ndChamberQueryResult';
-// export * from './bigMockQueryResults';
-// export * from './mockQueryResults';
-// export * from './smallFlightsQueryResults';
-// export * from './mockMobilityQueryResult';
-// export * from './typesMockQueryResults';
+import { big2ndChamberQueryResult, smallFlightsQueryResults, mockLargeQueryResults } from '../../../mock-data';
+import { VisualizationPanel } from '../../visualizationPanel';
 
-import { VisualizationPanel } from '../../panel/visualization';
 import {
   assignNewGraphQueryResult,
   graphQueryResultSlice,
@@ -33,7 +16,7 @@ import {
 
 import { SchemaUtils } from '../../../schema/schema-utils';
 import { simpleSchemaAirportRaw } from '../../../mock-data/schema/simpleAirportRaw';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/MatrixVis',
@@ -54,7 +37,7 @@ const Component: Meta<typeof VisualizationPanel> = {
   ],
 };
 
-const Mockstore = configureStore({
+const Mockstore: any = configureStore({
   reducer: {
     schema: schemaSlice.reducer,
     graphQueryResult: graphQueryResultSlice.reducer,
@@ -85,9 +68,9 @@ export const TestWithData = {
             edges: [{ id: 'escape/escape', from: 'agent/007', to: 'villain', attributes: { name: 'Escape' } }],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
@@ -105,9 +88,9 @@ export const TestWithNoData = {
             edges: [],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
@@ -116,7 +99,7 @@ export const TestWithBig2ndChamber = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: big2ndChamberQueryResult } }));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
@@ -125,7 +108,7 @@ export const TestWithSmallFlights = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: smallFlightsQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
@@ -134,25 +117,7 @@ export const TestWithLargeQueryResult = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: mockLargeQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
-  },
-};
-
-export const TestWithRecommendationPersonActedInMovieQueryResult = {
-  args: { loading: false },
-  play: async () => {
-    const dispatch = Mockstore.dispatch;
-    dispatch(assignNewGraphQueryResult(recommendationPersonActedInMovieQueryResultPayload));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
-  },
-};
-
-export const TestWithSlackReactionToThreadedMessageQueryResult = {
-  args: { loading: false },
-  play: async () => {
-    const dispatch = Mockstore.dispatch;
-    dispatch(assignNewGraphQueryResult(slackReactionToThreadedMessageQueryResultPayload));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/matrix/matrixvis.module.scss b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.module.scss
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/matrixvis.module.scss
rename to libs/shared/lib/vis/visualizations/matrixvis/matrixvis.module.scss
diff --git a/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d307dcd99affad6d280fcb2797c40be3724f8313
--- /dev/null
+++ b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx
@@ -0,0 +1,44 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { useImmer } from 'use-immer';
+import { GraphQueryResult } from '../../../data-access/store';
+import { LinkType, NodeType } from './Types';
+import { MatrixPixi } from './components/MatrixPixi';
+import { VisualizationPropTypes, VISComponentType } from '../../types';
+
+export const MatrixVis = React.memo(({ data, ml, settings }: VisualizationPropTypes) => {
+  const ref = useRef<HTMLDivElement>(null);
+  const [graph, setGraph] = useImmer<GraphQueryResult | undefined>(undefined);
+  const [highlightNodes, setHighlightNodes] = useState<NodeType[]>([]);
+  const [highlightedLinks, setHighlightedLinks] = useState<LinkType[]>([]);
+
+  useEffect(() => {
+    if (data) {
+      setGraph(data);
+    }
+  }, [data, ml]);
+
+  return (
+    <>
+      <div className="h-full w-full overflow-hidden" ref={ref}>
+        <MatrixPixi graph={graph} highlightNodes={highlightNodes} highlightedLinks={highlightedLinks} localConfig={settings} />
+      </div>
+    </>
+  );
+});
+
+const displayName = 'Matrix';
+
+export const MatrixVisComponent: VISComponentType = {
+  displayName: displayName,
+  VIS: MatrixVis,
+  settings: {
+    marks: {
+      type: 'dropdown',
+      options: ['rect', 'circle'],
+      value: 'rect',
+      label: 'Configure Marks',
+    },
+  },
+};
+
+export default MatrixVisComponent;
diff --git a/libs/shared/lib/vis/visualizations/nodelink/index.ts b/libs/shared/lib/vis/visualizations/nodelink/index.ts
deleted file mode 100644
index e019d6524926250715515c0b8c44a1c6f2bd874e..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/visualizations/nodelink/index.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import { VISComponentType } from '../../types';
-import { displayName, NodeLinkVis, localConfigSchema } from './nodelinkvis';
-export type { NodeLinkProps } from './nodelinkvis';
-
-export const NodeLinkComponent: VISComponentType = {
-  displayName,
-  VIS: NodeLinkVis,
-  localConfigSchema: localConfigSchema,
-};
-
-export default NodeLinkComponent;
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss.d.ts b/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss.d.ts
deleted file mode 100644
index 3ad131d27289ad8fba89f23f050e3e3258726483..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-declare const classNames: {
-  readonly container: 'container';
-  readonly title: 'title';
-  readonly subtitle: 'subtitle';
-  readonly subsubtitle: 'subsubtitle';
-  readonly subContainer: 'subContainer';
-  readonly rulesContainer: 'rulesContainer';
-  readonly selectContainer: 'selectContainer';
-};
-export = classNames;
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLExport.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLExport.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLExport.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLExport.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLForce.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLForce.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLForce.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLForce.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLMachineLearning.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLMachineLearning.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLMachineLearning.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLMachineLearning.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLPixi.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLPixi.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLPopup.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLPopup.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/query2NL.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/query2NL.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/query2NL.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/query2NL.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/utils.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/utils.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/utils.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/utils.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.module.scss
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss
rename to libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.module.scss
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.stories.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.stories.tsx
similarity index 84%
rename from libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.stories.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.stories.tsx
index ea402d7a48d619c8d1a186bfeec9a430e9703427..22500ead35179fd727f733727946a235c8aa9e9c 100644
--- a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.stories.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import {
   assignNewGraphQueryResult,
   graphQueryResultSlice,
@@ -11,7 +11,7 @@ import {
 import { configureStore } from '@reduxjs/toolkit';
 import { Provider } from 'react-redux';
 import { big2ndChamberQueryResult, smallFlightsQueryResults, mockLargeQueryResults } from '../../../mock-data';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/NodeLinkVis',
@@ -59,9 +59,9 @@ export const TestWithData = {
             edges: [{ id: 'escape/escape', from: 'agent/007', to: 'villain', attributes: { name: 'Escape' } }],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
@@ -79,9 +79,9 @@ export const TestWithNoData = {
             edges: [],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
@@ -89,7 +89,7 @@ export const TestWithBig2ndChamber = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: big2ndChamberQueryResult } }));
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
@@ -98,7 +98,7 @@ export const TestWithSmallFlights = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: smallFlightsQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
@@ -107,7 +107,7 @@ export const TestWithLargeQueryResult = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: mockLargeQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx
similarity index 65%
rename from libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx
index 1ddefa86efb53be058160ffe7dbc71320798c461..9a6009e48130ebc770298d6dc6f83a380df57d1d 100644
--- a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx
+++ b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx
@@ -1,5 +1,4 @@
 import React, { useEffect, useRef, useState } from 'react';
-import * as PIXI from 'pixi.js';
 import { GraphType, LinkType, NodeType } from './types';
 import { NLPixi } from './components/NLPixi';
 import { parseQueryResult } from './components/query2NL';
@@ -7,31 +6,7 @@ import { useImmer } from 'use-immer';
 import { ML, setShortestPathSource, setShortestPathTarget } from '../../../data-access/store/mlSlice';
 import { VisualizationPropTypes, VISComponentType } from '../../types';
 
-export type NodeLinkProps = {};
-
-/** Return default radius */
-function Radius() {
-  return 1;
-}
-
-/** This is the interface of the NodeLinkComponentState. */
-export interface NodeLinkComponentState {
-  graph: GraphType;
-  width: number;
-  height: number;
-  stage: PIXI.Container;
-  links: PIXI.Graphics;
-  simulation: d3.Simulation<NodeType, LinkType>;
-  myRef: React.RefObject<HTMLDivElement>;
-  renderer: PIXI.IRenderer;
-  dragOffset: any;
-  panOffset: any;
-  scalexy: number;
-}
-
-export const displayName = 'NodeLinkVis';
-
-export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationPropTypes<typeof displayName>) => {
+export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationPropTypes) => {
   const ref = useRef<HTMLDivElement>(null);
   const [graph, setGraph] = useImmer<GraphType | undefined>(undefined);
   const [highlightNodes, setHighlightNodes] = useState<NodeType[]>([]);
@@ -39,8 +14,6 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
 
   useEffect(() => {
     if (data) {
-      console.debug('%cResult from graphQuery', 'color: aquamarine', data);
-
       setGraph(
         parseQueryResult(data, ml, {
           defaultX: (ref.current?.clientWidth || 1000) / 2,
@@ -51,12 +24,8 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
   }, [data, ml]);
 
   const onClickedNode = (node: NodeType, ml: ML) => {
-    console.log('shortestPath', graph, ml.shortestPath.enabled);
-
     if (graph) {
       if (ml.shortestPath.enabled) {
-        console.log('shortestPath');
-
         setGraph((draft) => {
           let _node = draft?.nodes.find((n) => n.id === node.id);
           if (!_node) return draft;
@@ -79,7 +48,6 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
             dispatch(setShortestPathSource(node.id));
             dispatch(setShortestPathTarget(undefined));
           }
-          console.log('shortestPath', _node);
           return draft;
         });
       }
@@ -97,19 +65,53 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
             onClickedNode(node, ml);
           }}
         />
-        {/* <VisConfigPanelComponent> */}
-        {/* <NodeLinkConfigPanelComponent
-               graph={this.state.graph}
-               nlViewModel={this.nodeLinkViewModel}
-            /> */}
-        {/*</VisConfigPanelComponent>*/}
-        {/*<VisConfigPanelComponent isLeft>*/}
-        {/*  <AttributesConfigPanel nodeLinkViewModel={this.nodeLinkViewModel} />*/}
-        {/* </VisConfigPanelComponent> */}
       </div>
     </>
   );
 });
 
-export const localConfigSchema = {};
-export default NodeLinkVis;
+export const NodeLinkComponent: VISComponentType = {
+  displayName: 'NodeLink',
+  VIS: NodeLinkVis,
+  settings: {
+    layout: {
+      value: 'Force directed',
+      type: 'dropdown',
+      label: 'Layout',
+      options: ['Force directed'],
+      description: 'Select a layout that is used for the visualization',
+    },
+  },
+  encodings: {
+    color: {
+      label: 'Node color',
+      element: 'node',
+      dimension: ['categorical', 'numerical'],
+      selector: 'Color',
+      description: 'Select a color for the nodes',
+    },
+    shape: {
+      label: 'Node shape',
+      element: 'node',
+      dimension: ['categorical'],
+      selector: 'Shape',
+      description: 'Select a shape for the nodes',
+    },
+    size: {
+      label: 'Node size',
+      element: 'node',
+      dimension: ['categorical'],
+      selector: 'Size',
+      description: 'Select a size for the nodes',
+    },
+  },
+  interactions: {
+    showPopUpOnHover: {
+      value: true,
+      type: 'boolean',
+      label: 'Show pop-up',
+    },
+  },
+};
+
+export default NodeLinkComponent;
diff --git a/libs/shared/lib/vis/visualizations/nodelink/types.ts b/libs/shared/lib/vis/visualizations/nodelinkvis/types.ts
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/types.ts
rename to libs/shared/lib/vis/visualizations/nodelinkvis/types.ts
diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.stories.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.stories.tsx
index 96dc4eaa89e94a9654726d5b496cb8b2d674c2e2..b6252719dfe1d4ceeb5f6e85a28a7d0e15c2e369 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.stories.tsx
@@ -8,12 +8,12 @@ import {
 } from '../../../data-access/store';
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import { Provider } from 'react-redux';
 import { SchemaUtils } from '../../../schema/schema-utils';
 import { bigMockQueryResults, simpleSchemaRaw, smallFlightsQueryResults } from '../../../mock-data';
 import { simpleSchemaAirportRaw } from '../../../mock-data/schema/simpleAirportRaw';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/Paohvis',
@@ -86,9 +86,9 @@ export const TestWithData = {
             ],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.Paohvis));
+    dispatch(setActiveVisualization('PaohVis'));
   },
 };
 
@@ -99,7 +99,7 @@ export const TestWithAirportSimple = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: smallFlightsQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Paohvis));
+    dispatch(setActiveVisualization('PaohVis'));
   },
 };
 
@@ -110,7 +110,7 @@ export const TestWithAirport = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: bigMockQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Paohvis));
+    dispatch(setActiveVisualization('PaohVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
index 0e81c7bfb3be9cb71357bd0360a70da2d9d061b7..52f4d24a8869f936a0a50faf112a73dec6e1b08d 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
+++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
@@ -638,12 +638,10 @@ export const PaohVis = (props: PaohVisProps) => {
   );
 };
 
-const localConfigSchema = {};
-
 export const PaohVisComponent: VISComponentType = {
-  displayName: 'PaohVis',
+  displayName: 'Paoh',
   VIS: PaohVis,
-  localConfigSchema: localConfigSchema,
+  settings: {},
 };
 
 export default PaohVisComponent;
diff --git a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.stories.tsx b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.stories.tsx
index cdcef1260d99f650775907816507ee021862b2ec..ff4929db61379f8376f6bd9411135243e33700b1 100644
--- a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.stories.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import {
   assignNewGraphQueryResult,
   graphQueryResultSlice,
@@ -13,7 +13,7 @@ import {
 import { configureStore } from '@reduxjs/toolkit';
 import { Provider } from 'react-redux';
 import { mockLargeQueryResults } from '../../../mock-data/query-result';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/RawJSONVIS',
@@ -59,9 +59,9 @@ export const SimpleData = {
             edges: [],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.RawJSON));
+    dispatch(setActiveVisualization('RawJSONVis'));
   },
 };
 
@@ -78,9 +78,9 @@ export const LargeData = {
           type: 'nodelink',
           payload: mockLargeQueryResults,
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.RawJSON));
+    dispatch(setActiveVisualization('RawJSONVis'));
   },
 };
 
@@ -88,7 +88,7 @@ export const Empty = {
   play: async () => {
     const dispatch = store.dispatch;
     dispatch(resetGraphQueryResults());
-    dispatch(setActiveVisualization(Visualizations.RawJSON));
+    dispatch(setActiveVisualization('RawJSONVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx
index a4deeeec0131a7f386e56cfe14655945c326a4cd..f5cf47861b1b9e6902e7d4c6748e7cc787ddf9f6 100644
--- a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx
+++ b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx
@@ -6,7 +6,7 @@ export interface RawJSONVisProps {}
 
 const displayName = 'RawJSONVis';
 
-export const RawJSONVis = React.memo(({ data }: VisualizationPropTypes<typeof displayName>) => {
+export const RawJSONVis = React.memo(({ data }: VisualizationPropTypes) => {
   useEffect(() => {
     console.log('update rawjson useEffect');
   }, [data]);
@@ -28,12 +28,9 @@ export const RawJSONVis = React.memo(({ data }: VisualizationPropTypes<typeof di
   );
 });
 
-const localConfigSchema = {};
-
 export const RawJSONComponent: VISComponentType = {
-  displayName: displayName,
+  displayName: 'RawJSON',
   VIS: RawJSONVis,
-  localConfigSchema: localConfigSchema,
+  settings: {},
 };
-
 export default RawJSONComponent;
diff --git a/libs/shared/lib/vis/visualizations/semanticsubstrates/semanticsubstrates.stories.tsx b/libs/shared/lib/vis/visualizations/semanticsubstrates/semanticsubstrates.stories.tsx
index 5d4f31bda93924e2857eeb51dc7f20e388c6c054..8984fc8b91d855ed52dbb288abf4d4e006c577f2 100644
--- a/libs/shared/lib/vis/visualizations/semanticsubstrates/semanticsubstrates.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/semanticsubstrates/semanticsubstrates.stories.tsx
@@ -9,11 +9,11 @@ import {
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
 import { Provider } from 'react-redux';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import { SchemaUtils } from '../../../schema/schema-utils';
 import { simpleSchemaAirportRaw } from '../../../mock-data/schema/simpleAirportRaw';
 import { bigMockQueryResults } from '../../../mock-data';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/SemanticSubstrates',
@@ -73,7 +73,7 @@ export const TestWithData = {
             ],
           },
         },
-      })
+      }),
     );
     // dispatch(setActiveVisualization(Visualizations.SemanticSubstrates));
   },
@@ -89,7 +89,7 @@ export const TestWithAirport = {
       assignNewGraphQueryResult({
         queryID: '1',
         result: { type: 'nodelink', payload: bigMockQueryResults },
-      })
+      }),
     );
     // dispatch(setActiveVisualization(Visualizations.SemanticSubstrates));
   },
diff --git a/libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss.d.ts b/libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss.d.ts
deleted file mode 100644
index 8798bcd00095ed6509688816c10f5c9399d76b8b..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-declare const classNames: {
-  readonly 'table-container': 'table-container';
-  readonly table: 'table';
-};
-export = classNames;
diff --git a/libs/shared/lib/vis/visualizations/table_vis/index.ts b/libs/shared/lib/vis/visualizations/table_vis/index.ts
deleted file mode 100644
index 87bfb9628c81e4cc1060a1b414d16a438818d2b4..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/visualizations/table_vis/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './tableVis';
diff --git a/libs/shared/lib/vis/visualizations/table_vis/components/Table.tsx b/libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx
similarity index 99%
rename from libs/shared/lib/vis/visualizations/table_vis/components/Table.tsx
rename to libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx
index dddec9dc4738776ea8970457d7b24b6364a8818f..20164229ef5ae13132ed84d10ed82fba6573b6ab 100644
--- a/libs/shared/lib/vis/visualizations/table_vis/components/Table.tsx
+++ b/libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx
@@ -106,7 +106,7 @@ export const Table = ({ data, itemsPerPage, showBarPlot }: TableProps) => {
     let categoryCounts = [];
     const firstRowData = data[0];
 
-    console.log('First row: ', firstRowData);
+    // console.log('First row: ', firstRowData);
 
     let _data2Render = Object.keys(firstRowData.attribute).map((dataColumn: string, i) => {
       const newData2Render: Data2RenderI = {
@@ -187,7 +187,7 @@ export const Table = ({ data, itemsPerPage, showBarPlot }: TableProps) => {
       return newData2Render;
     });
 
-    console.log(_data2Render);
+    // console.log(_data2Render);
 
     setData2Render(_data2Render);
   }, [currentPage, data, sortedData]);
diff --git a/libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss b/libs/shared/lib/vis/visualizations/tablevis/components/table.module.scss
similarity index 100%
rename from libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss
rename to libs/shared/lib/vis/visualizations/tablevis/components/table.module.scss
diff --git a/libs/shared/lib/vis/visualizations/tablevis/index.ts b/libs/shared/lib/vis/visualizations/tablevis/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/libs/shared/lib/vis/visualizations/table_vis/tablevis.stories.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.stories.tsx
similarity index 86%
rename from libs/shared/lib/vis/visualizations/table_vis/tablevis.stories.tsx
rename to libs/shared/lib/vis/visualizations/tablevis/tablevis.stories.tsx
index c3d1e9c3c90e0a6438335c2c3b55339e055c0688..f75a72e75076ed83e668ec961243e211b4e2d7fa 100644
--- a/libs/shared/lib/vis/visualizations/table_vis/tablevis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.stories.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import {
   assignNewGraphQueryResult,
   graphQueryResultSlice,
@@ -21,7 +21,7 @@ import {
 
 import { SchemaUtils } from '../../../schema/schema-utils';
 import { simpleSchemaAirportRaw } from '../../../mock-data/schema/simpleAirportRaw';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/TableVis',
@@ -58,7 +58,7 @@ export const TestWithAirport = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: bigMockQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Table));
+    dispatch(setActiveVisualization('TableVis'));
   },
 };
 
@@ -69,7 +69,7 @@ export const TestWithBig2ndChamber = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: big2ndChamberQueryResult } }));
-    dispatch(setActiveVisualization(Visualizations.Table));
+    dispatch(setActiveVisualization('TableVis'));
   },
 };
 
@@ -80,7 +80,7 @@ export const TestWithTypesMock = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: typesMockQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Table));
+    dispatch(setActiveVisualization('TableVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/table_vis/tableVis.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx
similarity index 53%
rename from libs/shared/lib/vis/visualizations/table_vis/tableVis.tsx
rename to libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx
index f8ca400e5965c137b61556a0d39b001f18e06e48..d4ab3688d75c1a897d5548d53ef6986c6308c894 100644
--- a/libs/shared/lib/vis/visualizations/table_vis/tableVis.tsx
+++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx
@@ -1,16 +1,14 @@
 import React, { useMemo, useRef } from 'react';
 import { Table, AugmentedNodeAttributes } from './components/Table';
 import { SchemaAttribute } from '../../../schema';
-import { VisualizationPropTypes, VISComponentType, localConfigSchemaType } from '../../types';
+import { VisualizationPropTypes, VISComponentType } from '../../types';
 
 export type TableProps = {
   showBarplot: boolean;
   itemsPerPage: number;
 };
 
-const displayName = 'TableVis';
-
-export const TableVis = ({ data, schema, localConfig }: VisualizationPropTypes<typeof displayName>) => {
+export const TableVis = ({ data, schema, settings }: VisualizationPropTypes) => {
   const ref = useRef<HTMLDivElement>(null);
 
   const attributesArray = useMemo<AugmentedNodeAttributes[]>(
@@ -30,33 +28,29 @@ export const TableVis = ({ data, schema, localConfig }: VisualizationPropTypes<t
   );
 
   return (
-    <>
-      <div className="h-full w-full" ref={ref}>
-        {attributesArray.length > 0 && (
-          <Table data={attributesArray} itemsPerPage={localConfig.itemsPerPage} showBarPlot={localConfig.showBarplot} />
-        )}
-      </div>
-    </>
+    <div className="h-full w-full" ref={ref}>
+      {attributesArray.length > 0 && (
+        <Table data={attributesArray} itemsPerPage={settings.itemsPerPage} showBarPlot={settings.showBarplot} />
+      )}
+    </div>
   );
 };
 
-const localConfigSchema: localConfigSchemaType = {
-  showBarplot: {
-    value: true,
-    type: 'boolean',
-    label: 'Show barplot',
-  },
-  itemsPerPage: {
-    value: 10,
-    type: 'dropdown',
-    label: 'Items per page',
-    options: [10, 20, 30],
-  },
-};
-
 export const TableComponent: VISComponentType = {
-  displayName: displayName,
+  displayName: 'Table',
   VIS: TableVis,
-  localConfigSchema: localConfigSchema,
+  settings: {
+    showBarplot: {
+      value: true,
+      type: 'boolean',
+      label: 'Show barplot',
+    },
+    itemsPerPage: {
+      value: 10,
+      type: 'dropdown',
+      label: 'Items per page',
+      options: [10, 20, 30],
+    },
+  },
 };
 export default TableComponent;
diff --git a/package.json b/package.json
index 7b29d489fbc18be92634d4fe2ae0604e2abb5bdf..b03b7479a94430b4357b044fb3e3b3814816e3a4 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
     "sb": "turbo run sb --no-daemon",
     "lint": "turbo run lint --no-daemon",
     "test": "turbo run test --no-daemon",
-    "push": "turbo run lint test build-dev --no-daemon",
+    "push": "turbo run lint test build --no-daemon",
     "format": "prettier --write \"**/*.{ts,tsx,md,js}\"",
     "prepare": "husky install"
   },