diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/js/event_trigger.js b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/js/event_trigger.js
index f3dadbf89..6ff4ed920 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/js/event_trigger.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/js/event_trigger.js
@@ -7,6 +7,9 @@
 //
 //////////////////////////////////////////////////////////////
 
+import EventTriggerSchema from './event_trigger.ui';
+import { getNodeListByName, getNodeAjaxOptions } from '../../../../../../static/js/node_ajax';
+
 define('pgadmin.node.event_trigger', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
   'sources/pgadmin', 'pgadmin.browser',
@@ -64,6 +67,19 @@ define('pgadmin.node.event_trigger', [
         },
         ]);
       },
+      getSchema: function(treeNodeInfo, itemNodeData) {
+        return new EventTriggerSchema(
+          {
+            role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData),
+            function_names: ()=>getNodeAjaxOptions('fopts', this, treeNodeInfo, itemNodeData, {
+              cacheLevel: 'trigger_function',
+            }),
+          },
+          {
+            eventowner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name,
+          }
+        );
+      },
       // Define the model for event trigger node
       model: pgAdmin.Browser.Node.Model.extend({
         idAttribute: 'oid',
diff --git a/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/js/event_trigger.ui.js b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/js/event_trigger.ui.js
new file mode 100644
index 000000000..9b7710713
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/event_triggers/static/js/event_trigger.ui.js
@@ -0,0 +1,133 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2021, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import BaseUISchema from 'sources/SchemaView/base_schema.ui';
+import SecLabelSchema from '../../../../static/js/sec_label.ui';
+import { isEmptyString } from 'sources/validators';
+
+
+export default class EventTriggerSchema extends BaseUISchema {
+  constructor(fieldOptions={}, initValues) {
+    super({
+      oid: undefined,
+      name: undefined,
+      eventowner: undefined,
+      is_sys_obj: undefined,
+      comment: undefined,
+      enabled: 'O',
+      eventfuncoid: undefined,
+      eventfunname: undefined,
+      eventname: 'DDL_COMMAND_START',
+      when: undefined,
+      xmin: undefined,
+      source: undefined,
+      language: undefined,
+      ...initValues
+    });
+
+    this.fieldOptions = {
+      role: [],
+      function_names: [],
+      ...fieldOptions,
+    };
+
+  }
+
+  get idAttribute() {
+    return 'oid';
+  }
+
+
+  get baseFields() {
+    //let obj = this;
+    return [
+      {
+        id: 'name', label: gettext('Name'), cell: 'text',
+        type: 'text', noEmpty: true
+      },
+      {
+        id: 'oid', label: gettext('OID'), cell: 'text',
+        type: 'text', mode: ['properties'],
+      },{
+        id: 'eventowner', label: gettext('Owner'),
+        type: 'select', mode: ['properties', 'edit','create'], node: 'role',
+        options: this.fieldOptions.role
+      },
+      {
+        id: 'is_sys_obj', label: gettext('System event trigger?'),
+        cell:'switch', type: 'switch',
+        mode: ['properties'],
+      },
+      {
+        id: 'comment', label: gettext('Comment'), type: 'multiline',
+      },{
+        id: 'enabled', label: gettext('Trigger enabled?'),
+        group: gettext('Definition'), mode: ['properties', 'edit','create'],
+        options: [
+          {label: gettext('Enable'), value: 'O'},
+          {label: gettext('Disable'), value: 'D'},
+          {label: gettext('Replica'), value: 'R'},
+          {label: gettext('Always'), value: 'A'},
+        ],
+        type: 'select', controlProps: { allowClear: false, width: '100%' },
+      },{
+        id: 'eventfunname', label: gettext('Trigger function'),
+        type: 'select', group: gettext('Definition'),
+        options: this.fieldOptions.function_names
+      },{
+        id: 'eventname', label: gettext('Event'),
+        group: gettext('Definition'), cell: 'text',
+        options: [
+          {label: gettext('DDL COMMAND START'), value: 'DDL_COMMAND_START'},
+          {label: gettext('DDL COMMAND END'), value: 'DDL_COMMAND_END'},
+          {label: gettext('SQL DROP'), value: 'SQL_DROP'},
+        ],
+        type: 'select', controlProps: { allowClear: false, width: '100%' },
+      },
+      {
+        id: 'when', label: gettext('When TAG in'),  cell: 'string',
+        type: 'sql', group: gettext('Definition'),
+        controlProps: {className:['custom_height_css_class']},
+      },
+      {
+        id: 'seclabels', label: gettext('Security labels'), type: 'collection',
+        schema: new SecLabelSchema(),
+        editable: false, group: gettext('Security'),
+        mode: ['edit', 'create'],
+        canAdd: true, canEdit: false, canDelete: true,
+        uniqueCol : ['provider'],
+        min_version: 90200,
+      }
+    ];
+  }
+
+  validate(state, setError) {
+    let errmsg = null;
+
+    if (isEmptyString(state.service)) {
+
+      /* Event function name validation*/
+      if (isEmptyString(state.eventfunname)) {
+        errmsg = gettext('Event trigger function cannot be empty.');
+        setError('eventfunname', errmsg);
+        return true;
+      } else {
+        errmsg = null;
+        setError('eventfunname', errmsg);
+      }
+
+    } else {
+      errmsg = null;
+      _.each(['eventfunname'], (item) => {
+        setError(item, errmsg);
+      });
+    }
+  }
+}
diff --git a/web/pgadmin/static/js/SchemaView/FormView.jsx b/web/pgadmin/static/js/SchemaView/FormView.jsx
index 1b78cf2bb..58b752de4 100644
--- a/web/pgadmin/static/js/SchemaView/FormView.jsx
+++ b/web/pgadmin/static/js/SchemaView/FormView.jsx
@@ -56,6 +56,7 @@ function SQLTab({active, getSQLValue}) {
     options={{
       readOnly: true,
     }}
+    isAsync={true}
   />;
 }
 
diff --git a/web/pgadmin/static/js/SchemaView/MappedControl.jsx b/web/pgadmin/static/js/SchemaView/MappedControl.jsx
index 6195d610f..182519054 100644
--- a/web/pgadmin/static/js/SchemaView/MappedControl.jsx
+++ b/web/pgadmin/static/js/SchemaView/MappedControl.jsx
@@ -10,7 +10,7 @@
 import React, { useCallback } from 'react';
 import _ from 'lodash';
 
-import { FormInputText, FormInputSelect, FormInputSwitch, FormInputCheckbox, InputSQL, FormInputColor, FormInputFileSelect, FormInputToggle, InputSwitch } from '../components/FormComponents';
+import { FormInputText, FormInputSelect, FormInputSwitch, FormInputCheckbox, FormInputColor, FormInputFileSelect, FormInputToggle, InputSwitch, FormInputSQL } from '../components/FormComponents';
 import { InputSelect, InputText } from '../components/FormComponents';
 import Privilege from '../components/Privilege';
 import { evalFunc } from 'sources/utils';
@@ -28,6 +28,10 @@ function MappedFormControlBase({type, value, id, onChange, className, visible, i
     onChange && onChange(value);
   });
 
+  const onSqlChange = useCallback((e, cm) => {
+    onChange && onChange(cm.getValue());
+  });
+
   const onIntChange = useCallback((e) => {
     let value = e;
     if(e && e.target) {
@@ -73,7 +77,7 @@ function MappedFormControlBase({type, value, id, onChange, className, visible, i
   case 'file':
     return <FormInputFileSelect name={name} value={value} onChange={onTextChange} className={className} {...props} />;
   case 'sql':
-    return <InputSQL name={name} value={value} onChange={onTextChange} className={className} {...props}/>;
+    return <FormInputSQL name={name} value={value} onChange={onSqlChange} className={className} {...props}/>;
   default:
     return <></>;
   }
diff --git a/web/pgadmin/static/js/components/CodeMirror.jsx b/web/pgadmin/static/js/components/CodeMirror.jsx
index 6fac7e2cd..cb03ab18f 100644
--- a/web/pgadmin/static/js/components/CodeMirror.jsx
+++ b/web/pgadmin/static/js/components/CodeMirror.jsx
@@ -9,26 +9,57 @@
 
 import React, { useEffect, useRef } from 'react';
 import {default as OrigCodeMirror} from 'bundled_codemirror';
+import {useOnScreen} from 'sources/custom_hooks';
 import PropTypes from 'prop-types';
 
 /* React wrapper for CodeMirror */
-export default function CodeMirror({name, value, options}) {
+export default function CodeMirror({name, value, options, events, ...props}) {
   const taRef = useRef();
   const cmObj = useRef();
+  const cmWrapper = useRef();
+  const isVisibleTrack = useRef();
 
   useEffect(()=>{
     /* Create the object only once on mount */
     cmObj.current = new OrigCodeMirror.fromTextArea(
       taRef.current, options);
+
+    if(cmObj.current) {
+      try {
+        cmWrapper.current = cmObj.current.getWrapperElement();
+      } catch(e) {
+        cmWrapper.current = null;
+      }
+    }
+
+    Object.keys(events||{}).forEach((eventName)=>{
+      cmObj.current.on(eventName, events[eventName]);
+    });
   }, []);
 
   useEffect(()=>{
+    /* Refresh when value changes async */
+    if(props.isAsync) {
+      if(cmObj.current) {
+        cmObj.current.setValue(value);
+        cmObj.current.refresh();
+      }
+    }
+  }, [value]);
+
+  const onScreenVisible = useOnScreen(cmWrapper);
+  if(!isVisibleTrack.current && onScreenVisible) {
+    isVisibleTrack.current = true;
+
     /* Refresh when value changes */
     if(cmObj.current) {
       cmObj.current.setValue(value);
       cmObj.current.refresh();
     }
-  }, [value]);
+    cmObj.current.refresh();
+  } else if(!onScreenVisible) {
+    isVisibleTrack.current = false;
+  }
 
   return <textarea ref={taRef} name={name} />;
 }
@@ -36,5 +67,8 @@ export default function CodeMirror({name, value, options}) {
 CodeMirror.propTypes = {
   name: PropTypes.string,
   value: PropTypes.string,
-  options: PropTypes.object
+  options: PropTypes.object,
+  change: PropTypes.func,
+  events: PropTypes.object,
+  isAsync: PropTypes.bool
 };
diff --git a/web/pgadmin/static/js/components/FormComponents.jsx b/web/pgadmin/static/js/components/FormComponents.jsx
index 2307dde78..2c63c98be 100644
--- a/web/pgadmin/static/js/components/FormComponents.jsx
+++ b/web/pgadmin/static/js/components/FormComponents.jsx
@@ -127,8 +127,9 @@ FormInput.propTypes = {
   testcid: PropTypes.any,
 };
 
-export function InputSQL({value, options}) {
+export function InputSQL({value, options, onChange, ...props}) {
   const classes = useStyles();
+
   return (
     <CodeMirror
       value={value||''}
@@ -138,18 +139,26 @@ export function InputSQL({value, options}) {
         ...options,
       }}
       className={classes.sql}
+      events={{
+        change: (cm)=>{
+          onChange && onChange(cm.getValue(), cm);
+        },
+      }}
+      {...props}
     />
   );
 }
 InputSQL.propTypes = {
   value: PropTypes.string,
   options: PropTypes.object,
+  onChange: PropTypes.func
+
 };
 
-export function FormInputSQL({hasError, required, label, className, helpMessage, testcid, value, controlProps}) {
+export function FormInputSQL({hasError, required, label, className, helpMessage, testcid, value, controlProps, ...props}) {
   return (
-    <FormInput required={required} label={label} error={hasError} className={className} helpMessage={helpMessage} testcid={testcid}>
-      <InputSQL value={value} options={controlProps}/>
+    <FormInput required={required} label={label} error={hasError} className={className} helpMessage={helpMessage} testcid={testcid} >
+      <InputSQL value={value} options={controlProps} {...props}/>
     </FormInput>
   );
 }
@@ -162,6 +171,7 @@ FormInputSQL.propTypes = {
   testcid: PropTypes.string,
   value: PropTypes.string,
   controlProps: PropTypes.object,
+  change: PropTypes.func,
 };
 
 
diff --git a/web/pgadmin/static/js/custom_hooks.js b/web/pgadmin/static/js/custom_hooks.js
index b99dcbab4..488f87bd7 100644
--- a/web/pgadmin/static/js/custom_hooks.js
+++ b/web/pgadmin/static/js/custom_hooks.js
@@ -1,4 +1,4 @@
-import {useRef, useEffect} from 'react';
+import {useRef, useEffect, useState} from 'react';
 
 /* React hook for setInterval */
 export function useInterval(callback, delay) {
@@ -39,3 +39,21 @@ export function useDelayDebounce(callback, args, delay) {
   }, [args]);
 }
 
+export function useOnScreen(ref) {
+  const [isIntersecting, setIntersecting] = useState(false);
+  const observer = new IntersectionObserver(
+    ([entry]) => {
+      setIntersecting(entry.isIntersecting);
+    }
+  );
+  useEffect(() => {
+    if (ref.current) {
+      observer.observe(ref.current);
+    }
+    // Remove the observer as soon as the component is unmounted
+    return () => { observer.disconnect(); };
+  }, []);
+
+  return isIntersecting;
+}
+
diff --git a/web/regression/javascript/components/CodeMirror.spec.js b/web/regression/javascript/components/CodeMirror.spec.js
index 1adfe8145..5b347ea6c 100644
--- a/web/regression/javascript/components/CodeMirror.spec.js
+++ b/web/regression/javascript/components/CodeMirror.spec.js
@@ -26,6 +26,7 @@ describe('CodeMirror', ()=>{
     cmInstance = mount(
       <CodeMirror
         value={'Init text'}
+        isAsync={true}
         options={options}
         className="testClass"
       />);
diff --git a/web/regression/javascript/schema_ui_files/event_trigger.ui.spec.js b/web/regression/javascript/schema_ui_files/event_trigger.ui.spec.js
new file mode 100644
index 000000000..ac1faaa7a
--- /dev/null
+++ b/web/regression/javascript/schema_ui_files/event_trigger.ui.spec.js
@@ -0,0 +1,111 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2021, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import jasmineEnzyme from 'jasmine-enzyme';
+import React from 'react';
+import '../helper/enzyme.helper';
+import { createMount } from '@material-ui/core/test-utils';
+import pgAdmin from 'sources/pgadmin';
+import {messages} from '../fake_messages';
+import SchemaView from '../../../pgadmin/static/js/SchemaView';
+import EventTriggerSchema from '../../../pgadmin/browser/server_groups/servers/databases/event_triggers/static/js/event_trigger.ui';
+
+
+describe('EventTriggerSchema', ()=>{
+  let mount;
+  let schemaObj = new EventTriggerSchema(
+    {
+      role: ()=>[],
+      function_names: ()=>[],
+    },
+    {
+      eventowner: 'postgres'
+    }
+  );
+  let getInitData = ()=>Promise.resolve({});
+
+  /* Use createMount so that material ui components gets the required context */
+  /* https://material-ui.com/guides/testing/#api */
+  beforeAll(()=>{
+    mount = createMount();
+  });
+
+  afterAll(() => {
+    mount.cleanUp();
+  });
+
+  beforeEach(()=>{
+    jasmineEnzyme();
+    /* messages used by validators */
+    pgAdmin.Browser = pgAdmin.Browser || {};
+    pgAdmin.Browser.messages = pgAdmin.Browser.messages || messages;
+    pgAdmin.Browser.utils = pgAdmin.Browser.utils || {};
+  });
+
+  it('create', ()=>{
+    mount(<SchemaView
+      formType='dialog'
+      schema={schemaObj}
+      viewHelperProps={{
+        mode: 'create',
+      }}
+      onSave={()=>{}}
+      onClose={()=>{}}
+      onHelp={()=>{}}
+      onEdit={()=>{}}
+      onDataChange={()=>{}}
+      confirmOnCloseReset={false}
+      hasSQL={false}
+      disableSqlHelp={false}
+    />);
+  });
+
+  it('edit', ()=>{
+    mount(<SchemaView
+      formType='dialog'
+      schema={schemaObj}
+      getInitData={getInitData}
+      viewHelperProps={{
+        mode: 'create',
+      }}
+      onSave={()=>{}}
+      onClose={()=>{}}
+      onHelp={()=>{}}
+      onEdit={()=>{}}
+      onDataChange={()=>{}}
+      confirmOnCloseReset={false}
+      hasSQL={false}
+      disableSqlHelp={false}
+    />);
+  });
+
+  it('properties', ()=>{
+    mount(<SchemaView
+      formType='tab'
+      schema={schemaObj}
+      getInitData={getInitData}
+      viewHelperProps={{
+        mode: 'properties',
+      }}
+      onHelp={()=>{}}
+      onEdit={()=>{}}
+    />);
+  });
+
+  it('validate', ()=>{
+    let state = {};
+    let setError = jasmine.createSpy('setError');
+
+    state.eventfunname = null;
+    schemaObj.validate(state, setError);
+    expect(setError).toHaveBeenCalledWith('eventfunname', 'Event trigger function cannot be empty.');
+
+  });
+});
+
