diff --git a/react-ui/package.json b/react-ui/package.json
index 8bdbbfa50034042e846a40bf3bec7c00b6cd4161..76af9e08b8816b67d32f7057f361cf5e5551c8e6 100755
--- a/react-ui/package.json
+++ b/react-ui/package.json
@@ -26,6 +26,7 @@
         "react-dom": "^18.3.1",
         "react-error-boundary": "^4.1.2",
         "react-grid-layout": "^1.5.0",
+        "react-hook-form": "^7.54.2",
         "react-i18next": "^15.0.0",
         "react-loading-skeleton": "^3.5.0",
         "react-redux": "^9.1.2",
diff --git a/react-ui/src/.prettierrc b/react-ui/src/.prettierrc
new file mode 100644
index 0000000000000000000000000000000000000000..222861c341540db6785e10c2b6461d5cbc94a6eb
--- /dev/null
+++ b/react-ui/src/.prettierrc
@@ -0,0 +1,4 @@
+{
+  "tabWidth": 2,
+  "useTabs": false
+}
diff --git a/react-ui/src/components/devices/reducer/plugin.reducer.ts b/react-ui/src/components/devices/reducer/plugin.reducer.ts
index 1a09876fe65dce2f919dc58a46c60a37c14f03b4..f11f24477aed8e0673653c81f4f5eaff88c4cd73 100644
--- a/react-ui/src/components/devices/reducer/plugin.reducer.ts
+++ b/react-ui/src/components/devices/reducer/plugin.reducer.ts
@@ -1,15 +1,10 @@
 import {
     NetworkelementFlattenedManagedNetworkElement,
     NetworkelementManagedNetworkElement,
-    PndPrincipalNetworkDomain
+    PluginRegistryPlugin
 } from '@api/api'
 import { createSlice, PayloadAction } from '@reduxjs/toolkit'
-import { refreshUpdateTimer } from '@shared/reducer/routine.reducer'
-import { Category, CategoryType } from '@shared/types/category.type'
-import { REHYDRATE } from 'redux-persist'
-import { RootState } from 'src/stores'
 import '../routines/index'
-import { startListening } from '/src/stores/middleware/listener.middleware'
 
 export type Device = NetworkelementFlattenedManagedNetworkElement
 
@@ -19,14 +14,11 @@ interface SelectedObject {
     json: JSON | null
 }
 
-export interface DeviceSliceState {
-    devices: Device[]
-    pnds: PndPrincipalNetworkDomain[]
-
-    selected: SelectedObject | null
+export interface PluginSliceState {
+    plugins: Array<PluginRegistryPlugin>
 }
 
-const initialState: DeviceSliceState = {
+const initialState: PluginSliceState = {
     plugins: [],
 }
 
@@ -37,63 +29,18 @@ interface SetSelectedDeviceType {
     }
 }
 
-const deviceSlice = createSlice({
+const pluginSlice = createSlice({
     name: 'plugins',
     initialState,
     reducers: {
-        setPlugins: (state, action: PayloadAction<Plugin[] | undefined>) => {
-            state.devices = action.payload || []
+        setPlugins: (state, action: PayloadAction<PluginRegistryPlugin[] | undefined>) => {
+            state.plugins = action.payload || []
         },
     },
 })
 
-export const { setDevices, setSelectedDevice, setSelectedMne, setSelectedJson, setPnds } =
-    deviceSlice.actions
-
-export default deviceSlice.reducer
-export const deviceReducerPath = deviceSlice.reducerPath
-
-// add default selected device if no selected device is set
-startListening({
-    predicate: (action) => setDevices.match(action),
-    effect: async (action, listenerApi) => {
-        const { device: state } = listenerApi.getOriginalState() as RootState
-        if (state.selected) {
-            return
-        }
-
-        // if there are no devices available do set null
-        const device = action.payload?.[0] || null
-        listenerApi.dispatch(setSelectedDevice({ device } as SetSelectedDeviceType))
-    },
-})
-
-startListening({
-    predicate: (action) => setSelectedMne.match(action),
-    effect: async (action, listenerApi) => {
-        listenerApi.dispatch(refreshUpdateTimer(Category.TAB as CategoryType))
-    },
-})
+export const { setPlugins } =
+    pluginSlice.actions
 
-startListening({
-    predicate: (action) => setDevices.match(action),
-    effect: async (action, listenerApi) => {
-        listenerApi.dispatch(refreshUpdateTimer(Category.DEVICE as CategoryType))
-    },
-})
-
-/**
- * On startup reset the selected device 
- */
-startListening({
-    predicate: ({ type }: any) => type === REHYDRATE,
-    effect: async (_, listenerApi) => {
-        const { device: state } = listenerApi.getState() as RootState
-        const device = state.selected?.device
-        if (!device) {
-            return
-        }
-
-        listenerApi.dispatch(setSelectedDevice({ device, options: { bypassCheck: true } } as SetSelectedDeviceType))
-    },
-})
\ No newline at end of file
+export default pluginSlice.reducer
+export const pluginReducerPath = pluginSlice.reducerPath
diff --git a/react-ui/src/components/devices/routines/mne.routine.ts b/react-ui/src/components/devices/routines/mne.routine.ts
index 2928693075a75bf7af1044d44a91488b56e10b23..967da2615e5ccad3a80995d5dc8eebd7e72a0b0a 100755
--- a/react-ui/src/components/devices/routines/mne.routine.ts
+++ b/react-ui/src/components/devices/routines/mne.routine.ts
@@ -13,7 +13,6 @@ import { startListening } from '../../../stores/middleware/listener.middleware'
 
 export const FETCH_MNE_ACTION = 'subscription/device/fetchSelectedMNE'
 
-
 /**
  * #0
  * Trigger fetch MNE (#1)
diff --git a/react-ui/src/components/devices/routines/plugin.routine.ts b/react-ui/src/components/devices/routines/plugin.routine.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ece4608f7ba8caf8a0d25558ca37532d25e6a6eb
--- /dev/null
+++ b/react-ui/src/components/devices/routines/plugin.routine.ts
@@ -0,0 +1,36 @@
+import { PluginInternalServiceGetAvailablePluginsApiArg, api } from "@api/api"
+import { warnMessage } from "@helper/debug"
+import { createAsyncThunk } from "@reduxjs/toolkit"
+import { RootState } from "src/stores"
+import { setPlugins } from "../reducer/plugin.reducer"
+
+const FETCH_PLUGIN_ACTION = 'subscription/plugin/fetchPlugins'
+
+
+export const fetchPluginsThunk = createAsyncThunk(
+    FETCH_PLUGIN_ACTION,
+    async (_, thunkApi) => {
+        const { user } = thunkApi.getState() as RootState
+
+        if (!user.user?.roles) {
+            warnMessage('Plugin fetch was triggered but user data is not presence')
+            return
+        }
+
+        const payload: PluginInternalServiceGetAvailablePluginsApiArg = {
+            timestamp: new Date().getTime().toString(),
+        }
+
+
+        const plugins = await thunkApi.dispatch(api.endpoints.pluginInternalServiceGetAvailablePlugins.initiate(payload))
+
+        if (plugins.error || !plugins.data?.plugins) {
+            warnMessage('Plugin fetch returned an error: ' + plugins.error)
+            return
+        }
+
+        thunkApi.dispatch(
+            setPlugins(plugins.data?.plugins)
+        )
+    }
+)
\ No newline at end of file
diff --git a/react-ui/src/components/devices/view/device.view.list.tsx b/react-ui/src/components/devices/view/device.view.list.tsx
index 53f1580d13d36784df38242e4be998b2e0ae6590..868f26959038dc2b73ddb4d07c2ced774d3a3fd9 100755
--- a/react-ui/src/components/devices/view/device.view.list.tsx
+++ b/react-ui/src/components/devices/view/device.view.list.tsx
@@ -1,94 +1,122 @@
 import { insertMarkTags } from "@helper/text";
 import { useAppSelector } from "@hooks";
-import DOMPurify from 'dompurify';
-import { RefObject, useCallback, useRef } from 'react';
-import { Col, OverlayTrigger, Row, Tooltip } from 'react-bootstrap';
+import DOMPurify from "dompurify";
+import { RefObject, useCallback, useRef } from "react";
+import { Col, OverlayTrigger, Row, Tooltip } from "react-bootstrap";
 import { useTranslation } from "react-i18next";
 import { Device } from "../reducer/device.reducer";
 import { useDeviceTableViewModel } from "../view_model/device.list.viewmodel";
 
 const cropUUID = (uuid: string): string => {
-    return uuid.substring(0, 3) + "..." + uuid.substring(uuid.length - 3, uuid.length);
+  return (
+    uuid.substring(0, 3) + "..." + uuid.substring(uuid.length - 3, uuid.length)
+  );
 };
 
-export const DeviceList = ({ searchRef }: { searchRef: RefObject<HTMLInputElement> }) => {
-    const { devices, pnds, selected: selectedDevice } = useAppSelector(state => state.device);
-    const { t } = useTranslation('common');
-    const listRef = useRef<HTMLDivElement>(null);
-    const { dispatchDevice } = useDeviceTableViewModel(searchRef, listRef);
+export const DeviceList = ({
+  searchRef,
+}: {
+  searchRef: RefObject<HTMLInputElement>;
+}) => {
+  const {
+    devices,
+    pnds,
+    selected: selectedDevice,
+  } = useAppSelector((state) => state.device);
+  const { t } = useTranslation("common");
+  const listRef = useRef<HTMLDivElement>(null);
+  const { dispatchDevice } = useDeviceTableViewModel(searchRef, listRef);
 
-    const handleItemClick = useCallback((device: Device) => {
-        dispatchDevice(device)
-    }, []);
+  const handleItemClick = useCallback((device: Device) => {
+    dispatchDevice(device);
+  }, []);
 
-    const getDeviceList = useCallback(() => {
-        const search = searchRef?.current?.value;
-        let filtered = devices;
+  const getDeviceList = useCallback(() => {
+    const search = searchRef?.current?.value;
+    let filtered = devices;
 
-        if (search) {
-            filtered = devices.filter((device) => {
-                const user = pnds.find(pnd => pnd.id === device.pid);
-                return device.id?.includes(search) ||
-                    device.name?.includes(search) ||
-                    user?.name?.includes(search);
-            });
-        }
+    if (search) {
+      filtered = devices.filter((device) => {
+        const user = pnds.find((pnd) => pnd.id === device.pid);
+        return (
+          device.id?.includes(search) ||
+          device.name?.includes(search) ||
+          user?.name?.includes(search)
+        );
+      });
+    }
 
-        return filtered.map((device) => {
-            const user = pnds.find(pnd => pnd.id === device.pid);
-            const username = user?.name || '';
-            const deviceId = device.id!;
-            const croppedId = cropUUID(deviceId);
-            const devicename = device.name || '';
-            const isSelected = selectedDevice?.device.id === deviceId;
+    return filtered.map((device) => {
+      const user = pnds.find((pnd) => pnd.id === device.pid);
+      const username = user?.name || "";
+      const deviceId = device.id!;
+      const croppedId = cropUUID(deviceId);
+      const devicename = device.name || "";
+      const isSelected = selectedDevice?.device.id === deviceId;
 
-            return (
-                <div
-                    key={deviceId}
-                    className={`border-bottom border-primary p-2 transitions ${isSelected && 'bg-gradient-fade py-2'} ${!isSelected && 'text-disabled disabled-hover'}`}
-                    onClick={() => handleItemClick(device)}
-                >
-                    <Row
-                        className="align-items-center clickable"
-                        onClick={() => handleItemClick(device)}
-                    >
-                        <Col xs={12} sm={5} >
-                            <span dangerouslySetInnerHTML={{
-                                __html: search ? insertMarkTags(devicename, search) : DOMPurify.sanitize(devicename)
-                            }} />
-                        </Col>
-                        <Col xs={12} sm={3} >
-                            <OverlayTrigger overlay={<Tooltip id={deviceId}>{deviceId}</Tooltip>}>
-                                <span className="text-gray-500" dangerouslySetInnerHTML={{
-                                    __html: search ? insertMarkTags(croppedId, search) : DOMPurify.sanitize(croppedId)
-                                }} />
-                            </OverlayTrigger>
-                        </Col>
-                        <Col xs={12} sm={4} >
-                            <span className="text-gray-500" dangerouslySetInnerHTML={{
-                                __html: search ? insertMarkTags(username, search) : DOMPurify.sanitize(username)
-                            }} />
-                        </Col>
-                    </Row>
-                </div>
-            );
-        });
-    }, [devices, searchRef, pnds, selectedDevice, handleItemClick]);
-
-    return (
-        <div className="rounded border border-primary mt-2">
-            <Row className="border-bottom border-primary px-2 py-2 mx-0">
-                <Col xs={12} sm={5} >
-                    <span className="font-medium">{t('device.table.header.name')}</span>
-                </Col>
-                <Col xs={12} sm={3} >
-                    <span className="font-medium">{t('device.table.header.uuid')}</span>
-                </Col>
-                <Col xs={12} sm={4} >
-                    <span className="font-medium">{t('device.table.header.user')}</span>
-                </Col>
-            </Row>
-            <div ref={listRef}>{getDeviceList()}</div>
+      return (
+        <div
+          key={deviceId}
+          className={`border-bottom border-primary p-2 transitions ${isSelected && "bg-gradient-fade py-2"} ${!isSelected && "text-disabled disabled-hover"}`}
+          onClick={() => handleItemClick(device)}
+        >
+          <Row
+            className="align-items-center clickable"
+            onClick={() => handleItemClick(device)}
+          >
+            <Col xs={12} sm={5}>
+              <span
+                dangerouslySetInnerHTML={{
+                  __html: search
+                    ? insertMarkTags(devicename, search)
+                    : DOMPurify.sanitize(devicename),
+                }}
+              />
+            </Col>
+            <Col xs={12} sm={3}>
+              <OverlayTrigger
+                overlay={<Tooltip id={deviceId}>{deviceId}</Tooltip>}
+              >
+                <span
+                  className="text-gray-500"
+                  dangerouslySetInnerHTML={{
+                    __html: search
+                      ? insertMarkTags(croppedId, search)
+                      : DOMPurify.sanitize(croppedId),
+                  }}
+                />
+              </OverlayTrigger>
+            </Col>
+            <Col xs={12} sm={4}>
+              <span
+                className="text-gray-500"
+                dangerouslySetInnerHTML={{
+                  __html: search
+                    ? insertMarkTags(username, search)
+                    : DOMPurify.sanitize(username),
+                }}
+              />
+            </Col>
+          </Row>
         </div>
-    );
-};
\ No newline at end of file
+      );
+    });
+  }, [devices, searchRef, pnds, selectedDevice, handleItemClick]);
+
+  return (
+    <div className="rounded border border-primary mt-2">
+      <Row className="border-bottom border-primary px-2 py-2 mx-0">
+        <Col xs={12} sm={5}>
+          <span className="font-medium">{t("device.table.header.name")}</span>
+        </Col>
+        <Col xs={12} sm={3}>
+          <span className="font-medium">{t("device.table.header.uuid")}</span>
+        </Col>
+        <Col xs={12} sm={4}>
+          <span className="font-medium">{t("device.table.header.user")}</span>
+        </Col>
+      </Row>
+      <div ref={listRef}>{getDeviceList()}</div>
+    </div>
+  );
+};
diff --git a/react-ui/src/components/devices/view/device.view.tsx b/react-ui/src/components/devices/view/device.view.tsx
index 290697ac6a4f584bf77e62a8c7a564cf342dd251..814f51db1c77123e5b285131e48e5c94c65f40ed 100755
--- a/react-ui/src/components/devices/view/device.view.tsx
+++ b/react-ui/src/components/devices/view/device.view.tsx
@@ -1,69 +1,91 @@
-import { faCircleInfo, faPlus, faServer, faSliders } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { GridLayout } from '@layout/grid.layout/grid.layout';
-import { GridBox } from '@shared/components/box/gridBox.view';
-import { JsonViewer } from '@shared/components/json_viewer/view/json_viewer.view';
-import { useRef } from 'react';
-import { Button, Col, Form, Row } from 'react-bootstrap';
-import { useTranslation } from 'react-i18next';
-import { useDeviceViewModel } from '../view_model/device.viewmodel';
-import './device.scss';
-import { DeviceList } from './device.view.list';
-import { DeviceListCollapsable } from './subcomponent/device.view.list-detail';
+import {
+  faCircleInfo,
+  faPlus,
+  faServer,
+  faSliders,
+} from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { GridLayout } from "@layout/grid.layout/grid.layout";
+import { GridBox } from "@shared/components/box/gridBox.view";
+import { JsonViewer } from "@shared/components/json_viewer/view/json_viewer.view";
+import { useRef } from "react";
+import { Button, Col, Form, Row } from "react-bootstrap";
+import { useTranslation } from "react-i18next";
+import { useDeviceViewModel } from "../view_model/device.viewmodel";
+import "./device.scss";
+import { DeviceList } from "./device.view.list";
+import { DeviceListCollapsable } from "./subcomponent/device.view.list-detail";
+import AddDeviceModal from "./subcomponent/modal.view";
 
 const DeviceView = () => {
-    const { t } = useTranslation('common');
-    const searchRef = useRef<HTMLInputElement>(null);
-    const { jsonYang } = useDeviceViewModel();
+  const { t } = useTranslation("common");
+  const searchRef = useRef<HTMLInputElement>(null);
+  const { jsonYang, openAddModal, closeModal, addModal } = useDeviceViewModel();
 
-    return (
-        <GridLayout>
-            <>
-                <div key="device-list">
-                    <GridBox title={t("device.box.list.title")} title_icon={faServer}>
-                        <Row className="mb-3 align-items-center">
-                            <Col xs={12} md={6} lg={8}>
-                                <Form.Group controlId='device.search'>
-                                    <Form.Control type="text" placeholder={t('device.search.placeholder')} ref={searchRef} />
-                                </Form.Group>
-                            </Col>
-                            <Col xs={12} md={6} lg={4} className='mt-3 mt-md-0 text-md-end'>
-                                <Button variant='primary::button' className='btn-primary-button'>
-                                    <FontAwesomeIcon icon={faPlus} className='me-2' />
-                                    {t('device.add_device_button')}
-                                </Button>
-                            </Col>
-                        </Row>
-                        <Row>
-                            <Col xs={12} className='h-auto'>
-                                <DeviceList searchRef={searchRef} />
-                            </Col>
-                        </Row>
-                    </GridBox>
-                </div>
+  return (
+    <GridLayout>
+      <>
+        <div key="device-list">
+          <GridBox title={t("device.box.list.title")} title_icon={faServer}>
+            <Row className="mb-3 align-items-center">
+              <Col xs={12} md={6} lg={8}>
+                <Form.Group controlId="device.search">
+                  <Form.Control
+                    type="text"
+                    placeholder={t("device.search.placeholder")}
+                    ref={searchRef}
+                  />
+                </Form.Group>
+              </Col>
+              <Col xs={12} md={6} lg={4} className="mt-3 mt-md-0 text-md-end">
+                <Button
+                  variant="primary::button"
+                  className="btn-primary-button"
+                  onClick={() => openAddModal()}
+                >
+                  <FontAwesomeIcon icon={faPlus} className="me-2" />
+                  {t("device.add_device_button")}
+                </Button>
 
-                <div key="device-metadata">
-                    <GridBox title={t("device.box.information.title")} title_icon={faCircleInfo}>
-                        <Row>
-                            <Col xs={12} >
-                                <DeviceListCollapsable search={searchRef.current?.value || ''} />
-                            </Col>
-                        </Row>
-                    </GridBox>
-                </div>
+                <AddDeviceModal show={addModal} onHide={() => closeModal()} />
+              </Col>
+            </Row>
+            <Row>
+              <Col xs={12} className="h-auto">
+                <DeviceList searchRef={searchRef} />
+              </Col>
+            </Row>
+          </GridBox>
+        </div>
 
-                <div key="device-details">
-                    <GridBox title={t('device.box.configuration.title')} title_icon={faSliders}>
-                        <Row>
-                            <Col xs={12}>
-                                {jsonYang && <JsonViewer json={jsonYang} />}
-                            </Col>
-                        </Row>
-                    </GridBox>
-                </div>
-            </>
-        </GridLayout>
-    );
+        <div key="device-metadata">
+          <GridBox
+            title={t("device.box.information.title")}
+            title_icon={faCircleInfo}
+          >
+            <Row>
+              <Col xs={12}>
+                <DeviceListCollapsable
+                  search={searchRef.current?.value || ""}
+                />
+              </Col>
+            </Row>
+          </GridBox>
+        </div>
+
+        <div key="device-details">
+          <GridBox
+            title={t("device.box.configuration.title")}
+            title_icon={faSliders}
+          >
+            <Row>
+              <Col xs={12}>{jsonYang && <JsonViewer json={jsonYang} />}</Col>
+            </Row>
+          </GridBox>
+        </div>
+      </>
+    </GridLayout>
+  );
 };
 
-export default DeviceView;
\ No newline at end of file
+export default DeviceView;
diff --git a/react-ui/src/components/devices/view/subcomponent/modal.view.tsx b/react-ui/src/components/devices/view/subcomponent/modal.view.tsx
index d798e7adecd5190a5f7a9f413de0c41a4831e5eb..fd59364126c899baf67da2081de49bff879c8f0a 100644
--- a/react-ui/src/components/devices/view/subcomponent/modal.view.tsx
+++ b/react-ui/src/components/devices/view/subcomponent/modal.view.tsx
@@ -1,141 +1,162 @@
-import { NetworkelementAddListRequest, useNetworkElementServiceAddListMutation } from '@api/api';
-import React, { useState } from 'react';
-import { Button, Form, Modal } from 'react-bootstrap';
+import { useModalViewModel } from "@component/devices/view_model/modal.viewmodel";
+import { useAppSelector } from "@hooks";
+import React from "react";
+import { Alert, Button, Form, Modal } from "react-bootstrap";
+import { useTranslation } from "react-i18next";
 
 interface AddDeviceModalProps {
-    show: boolean;
-    onHide: () => void;
-}
-
-interface FormData {
-    address: '',
-    mneName: '',
-    transportOption: undefined,
-    gnmiSubscribePaths: [],
+  show: boolean;
+  onHide: () => void;
 }
 
 const AddDeviceModal: React.FC<AddDeviceModalProps> = ({ show, onHide }) => {
-    const [addNetworkElement] = useNetworkElementServiceAddListMutation();
-    const [formData, setFormData] = useState<FormData>({
-        address: '',
-        mneName: '',
-        transportOption: undefined,
-        gnmiSubscribePaths: [],
-    });
-
-    const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-        const { name, value } = e.target;
-        setFormData(prev => ({
-            ...prev,
-            [name]: value
-        }));
-    };
-
-    const handleSubmit = async (e: React.FormEvent) => {
-        e.preventDefault();
+  const { loading, register, handleSubmit, errors } = useModalViewModel({
+    hide: onHide,
+  });
+  const { plugins } = useAppSelector((state) => state.plugin);
+  const { t } = useTranslation("common");
 
-        const request: NetworkelementAddListRequest = {
-            timestamp: Date.now().toString(), // Convert to nanoseconds if needed
-            mne: [formData],
-            pid: formData.pid
-        };
+  return (
+    <Modal show={show} onHide={onHide} centered size="lg">
+      <Modal.Header closeButton>
+        <Modal.Title>{t("device.add_device.title")}</Modal.Title>
+      </Modal.Header>
+      <Modal.Body>
+        {errors.root && (
+          <Alert variant="danger" className="mt-3">
+            {errors.root.message || t("device.add_device.error")}
+          </Alert>
+        )}
+        <Form onSubmit={handleSubmit}>
+          <Form.Group className="mb-3">
+            <Form.Label>
+              {t("device.add_device.fields.mne_name.label")}
+            </Form.Label>
+            <Form.Control
+              type="text"
+              placeholder={t("device.add_device.fields.mne_name.placeholder")}
+              isInvalid={!!errors.mneName}
+              {...register("mneName", {
+                required: t("device.add_device.fields.mne_name.required"),
+              })}
+            />
+            <Form.Control.Feedback type="invalid">
+              {errors.mneName?.message}
+            </Form.Control.Feedback>
+          </Form.Group>
+          <Form.Group className="mb-3">
+            <Form.Label>
+              {t("device.add_device.fields.address.label")}
+            </Form.Label>
+            <Form.Control
+              type="text"
+              placeholder={t("device.add_device.fields.address.placeholder")}
+              isInvalid={!!errors.address}
+              {...register("transportOption.address", {
+                required: t("device.add_device.fields.address.required"),
+              })}
+            />
+            <Form.Control.Feedback type="invalid">
+              {errors.address?.message}
+            </Form.Control.Feedback>
+          </Form.Group>
 
-        try {
-            await addNetworkElement({ networkelementAddListRequest: request });
-            handleReset();
-            // You might want to add a success notification here
-        } catch (error) {
-            console.error('Failed to add device:', error);
-            // You might want to add an error notification here
-        }
-    };
+          <Form.Group className="mb-3">
+            <Form.Label>
+              {t("device.add_device.fields.plugin.label")}
+            </Form.Label>
+            <Form.Control
+              as="select"
+              isInvalid={!!errors.pluginId}
+              {...register("pluginId", {
+                required: t("device.add_device.fields.plugin.required"),
+              })}
+            >
+              <option value="">
+                {t("device.add_device.fields.plugin.placeholder")}
+              </option>
+              {plugins.map((plugin) => (
+                <option key={plugin.id} value={plugin.id}>
+                  {plugin.manifest?.name}
+                </option>
+              ))}
+            </Form.Control>
+            <Form.Control.Feedback type="invalid">
+              {errors.pluginId?.message}
+            </Form.Control.Feedback>
+          </Form.Group>
 
-    const handleReset = () => {
-        setFormData({
-            address: '',
-            pid: '',
-            pluginId: '',
-            mneName: '',
-            transportOption: undefined,
-            gnmiSubscribePaths: [],
-            mneId: ''
-        });
-        onHide();
-    };
+          <Form.Group className="mb-3">
+            <Form.Check
+              type="checkbox"
+              label={t("device.add_device.fields.tls.label")}
+              {...register("transportOption.tls")}
+            />
+          </Form.Group>
 
-    return (
-        <Modal show={show} onHide={handleReset} centered>
-            <Modal.Header closeButton>
-                <Modal.Title>Add New Device</Modal.Title>
-            </Modal.Header>
-            <Form onSubmit={handleSubmit}>
-                <Modal.Body>
-                    <Form.Group className="mb-3">
-                        <Form.Label>Address</Form.Label>
-                        <Form.Control
-                            type="text"
-                            name="address"
-                            value={formData.address}
-                            onChange={handleInputChange}
-                            placeholder="Enter device address"
-                        />
-                    </Form.Group>
+          <h5 className="mt-4">
+            {t("device.add_device.fields.credentials.title")}
+          </h5>
+          <Form.Group className="mb-3">
+            <Form.Label>
+              {t("device.add_device.fields.credentials.username.label")}
+            </Form.Label>
+            <Form.Control
+              type="text"
+              placeholder={t(
+                "device.add_device.fields.credentials.username.placeholder",
+              )}
+              isInvalid={!!errors.transportOption?.username}
+              {...register("transportOption.username", {
+                required: t(
+                  "device.add_device.fields.credentials.username.required",
+                ),
+              })}
+            />
+            <Form.Control.Feedback type="invalid">
+              {errors.transportOption?.username?.message}
+            </Form.Control.Feedback>
+          </Form.Group>
 
-                    <Form.Group className="mb-3">
-                        <Form.Label>PID</Form.Label>
-                        <Form.Control
-                            type="text"
-                            name="pid"
-                            value={formData.pid}
-                            onChange={handleInputChange}
-                            placeholder="Enter PID"
-                        />
-                    </Form.Group>
+          <Form.Group className="mb-3">
+            <Form.Label>
+              {t("device.add_device.fields.credentials.password.label")}
+            </Form.Label>
+            <Form.Control
+              type="password"
+              placeholder={t(
+                "device.add_device.fields.credentials.password.placeholder",
+              )}
+              isInvalid={!!errors.transportOption?.password}
+              {...register("transportOption.password", {
+                required: t(
+                  "device.add_device.fields.credentials.password.required",
+                ),
+              })}
+            />
+            <Form.Control.Feedback type="invalid">
+              {errors.transportOption?.password?.message}
+            </Form.Control.Feedback>
+          </Form.Group>
 
-                    <Form.Group className="mb-3">
-                        <Form.Label>Plugin ID</Form.Label>
-                        <Form.Control
-                            type="text"
-                            name="pluginId"
-                            value={formData.pluginId}
-                            onChange={handleInputChange}
-                            placeholder="Enter plugin ID"
-                        />
-                    </Form.Group>
-
-                    <Form.Group className="mb-3">
-                        <Form.Label>MNE Name</Form.Label>
-                        <Form.Control
-                            type="text"
-                            name="mneName"
-                            value={formData.mneName}
-                            onChange={handleInputChange}
-                            placeholder="Enter MNE name"
-                        />
-                    </Form.Group>
-
-                    <Form.Group className="mb-3">
-                        <Form.Label>MNE ID</Form.Label>
-                        <Form.Control
-                            type="text"
-                            name="mneId"
-                            value={formData.mneId}
-                            onChange={handleInputChange}
-                            placeholder="Enter MNE ID"
-                        />
-                    </Form.Group>
-                </Modal.Body>
-                <Modal.Footer>
-                    <Button variant="secondary" onClick={handleReset}>
-                        Cancel
-                    </Button>
-                    <Button variant="primary" type="submit">
-                        Add Device
-                    </Button>
-                </Modal.Footer>
-            </Form>
-        </Modal>
-    );
+          <div className="d-flex justify-content-end gap-2">
+            <Button variant="secondary" onClick={onHide}>
+              {t("device.add_device.buttons.cancel")}
+            </Button>
+            <Button disabled={loading} variant="primary" type="submit">
+              {loading ? (
+                <>
+                  <span className="spinner-border spinner-border-sm me-2" />
+                  {t("device.add_device.buttons.loading")}
+                </>
+              ) : (
+                t("device.add_device.buttons.submit")
+              )}
+            </Button>
+          </div>
+        </Form>
+      </Modal.Body>
+    </Modal>
+  );
 };
-
-export default AddDeviceModal;
\ No newline at end of file
+export default AddDeviceModal;
diff --git a/react-ui/src/components/devices/view_model/device.viewmodel.ts b/react-ui/src/components/devices/view_model/device.viewmodel.ts
index f2fd635533e7de2e4931964ba0333fcdad9a9969..5c4acc6619f1f7156e85ef7b5f393a29dee09276 100755
--- a/react-ui/src/components/devices/view_model/device.viewmodel.ts
+++ b/react-ui/src/components/devices/view_model/device.viewmodel.ts
@@ -1,8 +1,20 @@
-import { useAppSelector } from '@hooks'
-import { useMemo } from 'react'
+import { useAppDispatch, useAppSelector } from '@hooks'
+import { useMemo, useState } from 'react'
+import { fetchPluginsThunk } from '../routines/plugin.routine'
 
 export const useDeviceViewModel = () => {
+    const [addModal, setAddModal] = useState<boolean>(false)
     const { selected: selectedDevice } = useAppSelector((state) => state.device)
+    const dispatch = useAppDispatch()
+
+    const openAddModal = () => {
+        dispatch(fetchPluginsThunk())
+        setAddModal(true)
+    }
+
+    const closeModal = () => {
+        setAddModal(false)
+    }
 
     const getYangModelJSON = (): JSON | null => {
         if (!selectedDevice?.json) {
@@ -23,6 +35,9 @@ export const useDeviceViewModel = () => {
 
     return {
         jsonYang,
-        selectedDevice
+        selectedDevice,
+        openAddModal,
+        closeModal,
+        addModal
     }
 }
diff --git a/react-ui/src/components/devices/view_model/modal.viewmodel.ts b/react-ui/src/components/devices/view_model/modal.viewmodel.ts
index de9bbf68ecba77b9173afd144ac4e36775e22a8e..98742bd3495ba6ced883402a7d8dbbcf4e22f9ba 100644
--- a/react-ui/src/components/devices/view_model/modal.viewmodel.ts
+++ b/react-ui/src/components/devices/view_model/modal.viewmodel.ts
@@ -1,10 +1,94 @@
-import { useEffect } from "react"
+import { NetworkelementAddListRequest, NetworkelementSetMne, useNetworkElementServiceAddListMutation } from "@api/api";
+import { useAppDispatch, useAppSelector } from "@hooks";
+import { fetchUser } from "@shared/routine/user.routine";
+import { useState } from "react";
+import { SubmitHandler, useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
+import { toast } from "react-toastify";
 
+interface FormData {
+    address: string;
+    mneName: string;
+    pluginId: string;
+    transportOption: {
+        address: string;
+        username: string;
+        password: string;
+        tls: boolean;
+        gnmiTransportOption: Object;
+    };
+    gnmiSubscribePaths: string[];
+}
 
-export const useModalViewModel = () => {
+type ModalViewModelType = {
+    hide: () => void
+}
 
+export const useModalViewModel = ({ hide }: ModalViewModelType) => {
+    const dispatch = useAppDispatch()
+    const [addNetworkElement] = useNetworkElementServiceAddListMutation();
+    const [loading, setLoading] = useState<boolean>(false)
+    const { t } = useTranslation('common')
 
-    useEffect(() => {
+    const {
+        register,
+        handleSubmit,
+        setError,
+        clearErrors,
+        formState: { errors },
+        reset: resetModal
+    } = useForm<FormData>();
 
-    }, [])
+    const { user } = useAppSelector(state => state.user);
+
+    const reset = () => { resetModal(); hide(); }
+    const success = () => { toast.success(t('device.add_device.success')); reset(); dispatch(fetchUser()) }
+
+
+    const onSubmit: SubmitHandler<FormData> = async (data) => {
+        clearErrors()
+        setLoading(true)
+        const mne: NetworkelementSetMne = {
+            ...data,
+            gnmiSubscribePaths: [],
+            transportOption: {
+                ...data.transportOption,
+                gnmiTransportOption: {},
+            }
+        }
+
+        if (!user?.id) {
+            toast.error("global.error.missing_user")
+            return
+        }
+
+        const request: NetworkelementAddListRequest = {
+            timestamp: Date.now().toString(),
+            mne: [mne],
+            pid: user.id
+        };
+
+        try {
+            const response = await addNetworkElement({ networkelementAddListRequest: request });
+            setLoading(false)
+
+            if (response.error) {
+                const error = response.error as any
+                setError('root', error.data)
+                return
+            }
+
+            success()
+        } catch (error) {
+            setError('root', error?.data?.message || undefined)
+        }
+    }
+
+    return {
+        onSubmit,
+        handleSubmit: handleSubmit(onSubmit),
+        register,
+        errors,
+        loading
+    }
 }
\ No newline at end of file
diff --git a/react-ui/src/i18n/locales/en/translations.json b/react-ui/src/i18n/locales/en/translations.json
index 92fb59d85436770697e8f0640d0df683723c67ea..8a81a03e9c7b11ad84263a385ef38bd01c2d3d10 100755
--- a/react-ui/src/i18n/locales/en/translations.json
+++ b/react-ui/src/i18n/locales/en/translations.json
@@ -14,6 +14,9 @@
             },
             "box": {
                 "lastUpdate": "Last updated {{seconds}} seconds ago"
+            },
+            "error": {
+                "missing_user": "Error: User information. Please relogin and try it again"
             }
         },
         "json_viewer": {
@@ -43,6 +46,49 @@
                     "title": "Configuration"
                 }
             },
+            "add_device": {
+                "success": "Device successfully added",
+                "title": "Add New Device",
+                "error": "An error occurred on save",
+                "fields": {
+                    "address": {
+                        "label": "IPv4 address",
+                        "placeholder": "172.100.0.1",
+                        "required": "Ipv4 Address is required"
+                    },
+                    "mne_name": {
+                        "label": "MNE Name",
+                        "placeholder": "Enter MNE name",
+                        "required": "MNE name is required"
+                    },
+                    "plugin": {
+                        "label": "Plugin",
+                        "placeholder": "Select plugin...",
+                        "required": "Plugin selection is required"
+                    },
+                    "tls": {
+                        "label": "TLS Enabled"
+                    },
+                    "credentials": {
+                        "title": "Credentials",
+                        "username": {
+                            "label": "Username",
+                            "placeholder": "Enter username",
+                            "required": "Username is required"
+                        },
+                        "password": {
+                            "label": "Password",
+                            "placeholder": "Enter password",
+                            "required": "Password is required"
+                        }
+                    }
+                },
+                "buttons": {
+                    "cancel": "Cancel",
+                    "submit": "Add Device",
+                    "loading": "Adding..."
+                }
+            },
             "table": {
                 "header": {
                     "name": "Name",
diff --git a/react-ui/src/shared/api/api.ts b/react-ui/src/shared/api/api.ts
index 32969f1686479812253d387868a9350bd41a882c..e0f35f0b7c2db79d9eb066c2d7a1375642292b7d 100755
--- a/react-ui/src/shared/api/api.ts
+++ b/react-ui/src/shared/api/api.ts
@@ -4,6 +4,7 @@ export const addTagTypes = [
     'ConfigurationManagementService',
     'AuthService',
     'NetworkElementService',
+    'PluginInternalService',
     'PndService',
     'RoleService',
     'RoutingTableService',
@@ -214,6 +215,18 @@ const injectedRtkApi = api
                 }),
                 invalidatesTags: ['NetworkElementService'],
             }),
+            pluginInternalServiceGetAvailablePlugins: build.query<
+                PluginInternalServiceGetAvailablePluginsApiResponse,
+                PluginInternalServiceGetAvailablePluginsApiArg
+            >({
+                query: (queryArg) => ({
+                    url: `/plugins`,
+                    params: {
+                        timestamp: queryArg.timestamp,
+                    },
+                }),
+                providesTags: ['PluginInternalService'],
+            }),
             pndServiceGetPnd: build.query<
                 PndServiceGetPndApiResponse,
                 PndServiceGetPndApiArg
@@ -680,6 +693,11 @@ export type NetworkElementServiceUpdateApiResponse =
 export type NetworkElementServiceUpdateApiArg = {
     networkelementUpdateNetworkElementRequest: TodoChangeNameToFitTheRest
 }
+export type PluginInternalServiceGetAvailablePluginsApiResponse =
+    /** status 200 A successful response. */ GosdnpluginRegistryGetResponse
+export type PluginInternalServiceGetAvailablePluginsApiArg = {
+    timestamp?: string
+}
 export type PndServiceGetPndApiResponse =
     /** status 200 A successful response. */ PndGetPndResponse
 export type PndServiceGetPndApiArg = {
@@ -1183,6 +1201,11 @@ export type TodoChangeNameToFitTheRest = {
     timestamp?: string
     networkElement?: NetworkelementManagedNetworkElement
 }
+export type GosdnpluginRegistryGetResponse = {
+    /** Timestamp in nanoseconds since Epoch. */
+    timestamp?: string
+    plugins?: PluginRegistryPlugin[]
+}
 export type PndGetPndResponse = {
     /** Timestamp in nanoseconds since Epoch. */
     timestamp?: string
@@ -1432,6 +1455,7 @@ export const {
     useNetworkElementServiceGetIntendedPathQuery,
     useNetworkElementServiceGetPathQuery,
     useNetworkElementServiceUpdateMutation,
+    usePluginInternalServiceGetAvailablePluginsQuery,
     usePndServiceGetPndQuery,
     usePndServiceGetPndListQuery,
     usePndServiceCreatePndListMutation,
diff --git a/react-ui/src/stores/persist.store.ts b/react-ui/src/stores/persist.store.ts
index e12467ed9a3533da0cc1574e4fb9809e3858cb57..91d961cadc0283e2a530138096a7774e967ad6c0 100755
--- a/react-ui/src/stores/persist.store.ts
+++ b/react-ui/src/stores/persist.store.ts
@@ -1,4 +1,5 @@
 import deviceReducer from '@component/devices/reducer/device.reducer'
+import pluginReducer from '@component/devices/reducer/plugin.reducer'
 import jsonViewerReducer from '@shared/components/json_viewer/reducer/json_viewer.reducer'
 import routineReducer from '@shared/reducer/routine.reducer'
 import userReducer from '@shared/reducer/user.reducer'
@@ -7,6 +8,8 @@ import { persistReducer } from 'redux-persist'
 import storage from 'redux-persist/es/storage'
 import { emptySplitApi } from './api.store'
 
+
+
 /** local storage config */
 const rootPersistConfig = {
     key: 'root',
@@ -19,6 +22,7 @@ const rootReducer = combineReducers({
     device: deviceReducer,
     routine: routineReducer,
     json_viwer: jsonViewerReducer,
+    plugin: pluginReducer,
     [emptySplitApi.reducerPath]: emptySplitApi.reducer,
 })
 
diff --git a/react-ui/yarn.lock b/react-ui/yarn.lock
index 224cc0ddd12db6f8a8c6ff139a8f4015d76e03dc..5e4047ff1c1a76982bf2b4c0233b5da1adebda58 100755
--- a/react-ui/yarn.lock
+++ b/react-ui/yarn.lock
@@ -8887,6 +8887,11 @@ react-grid-layout@^1.5.0:
     react-resizable "^3.0.5"
     resize-observer-polyfill "^1.5.1"
 
+react-hook-form@^7.54.2:
+  version "7.54.2"
+  resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.54.2.tgz#8c26ed54c71628dff57ccd3c074b1dd377cfb211"
+  integrity sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==
+
 react-i18next@^15.0.0:
   version "15.1.4"
   resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.1.4.tgz#65c03c31a5e42202000652e163f22f23a9306a60"