How to Customize the Context Menu in Vue PDF Viewer

17 Aug 202624 minutes to read

The PDF Viewer supports adding custom options to the context menu using the addCustomMenu() method; define custom actions with customContextMenuSelect(). See the addCustomMenu() and customContextMenuSelect() API.

Add a custom option

The following example adds custom options to the context menu.

import { PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
         TextSelection, TextSearch, Annotation, FormFields, FormDesigner,PageOrganizer } from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";

export default {
    components: {
        'ejs-pdfviewer': PdfViewerComponent,
        'ejs-menu': MenuComponent,
        'ejs-checkbox': CheckBoxComponent
    },
    data: function() {
        return {
			documentPath:'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf',
            resourceUrl: 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib',
            menuItems : [
            {
                text: 'Search In Google',
                id: 'search_in_google',
                iconCss: 'e-icons e-search'
            },
            {
                text: 'Lock Annotation',
                iconCss: 'e-icons e-lock',
                id: 'lock_annotation'
            },
            {
                text: 'Unlock Annotation',
                iconCss: 'e-icons e-unlock',
                id: 'unlock_annotation'
            },
            {
                text: 'Lock Form Field',
                iconCss: 'e-icons e-lock',
                id: 'read_only_true'
            },
            {
                text: 'Unlock Form Field',
                iconCss: 'e-icons e-unlock',
                id: 'read_only_false'
            },
        ]
        }
    },
	provide: {
      PdfViewer: [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner,PageOrganizer]
    },
    methods: {
    documentLoad: function (args) {
        var viewer = this.$refs.pdfviewer.ej2Instances;
        viewer.addCustomMenu(this.menuItems, false, false);
    },
  }
}
import {
  PdfViewerComponent as EjsPdfviewer, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer
} from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent as EjsMenu } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent as EjsCheckbox } from "@syncfusion/ej2-vue-buttons";
import { provide, ref } from "vue";

const pdfviewer = ref(null);
const documentPath = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';
const resourceUrl = 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib';
const menuItems = [
  {
    text: 'Search In Google',
    id: 'search_in_google',
    iconCss: 'e-icons e-search'
  },
  {
    text: 'Lock Annotation',
    iconCss: 'e-icons e-lock',
    id: 'lock_annotation'
  },
  {
    text: 'Unlock Annotation',
    iconCss: 'e-icons e-unlock',
    id: 'unlock_annotation'
  },
  {
    text: 'Lock Form Field',
    iconCss: 'e-icons e-lock',
    id: 'read_only_true'
  },
  {
    text: 'Unlock Form Field',
    iconCss: 'e-icons e-unlock',
    id: 'read_only_false'
  },
]

provide('PdfViewer', [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer])

const documentLoad = function (args) {
  const viewer = pdfviewer.value.ej2Instances;
  viewer.addCustomMenu(this.menuItems, false, false);
}

Customize the default vs custom menu

Toggle the display of the default context menu. When the addCustomMenu parameter is true, the default menu is hidden; when false, default menu items are displayed alongside custom items.

import {
  PdfViewerComponent as EjsPdfviewer, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer
} from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent as EjsMenu } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent as EjsCheckbox, SwitchComponent as EjsSwitch } from "@syncfusion/ej2-vue-buttons";
import { provide, ref } from "vue";

const pdfviewer = ref(null);
const documentPath = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';
const resourceUrl = 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib';
const menuItems = [
  {
    text: 'Search In Google',
    id: 'search_in_google',
    iconCss: 'e-icons e-search'
  },
  {
    text: 'Lock Annotation',
    iconCss: 'e-icons e-lock',
    id: 'lock_annotation'
  },
  {
    text: 'Unlock Annotation',
    iconCss: 'e-icons e-unlock',
    id: 'unlock_annotation'
  },
  {
    text: 'Lock Form Field',
    iconCss: 'e-icons e-lock',
    id: 'read_only_true'
  },
  {
    text: 'Unlock Form Field',
    iconCss: 'e-icons e-unlock',
    id: 'read_only_false'
  },
]


provide('PdfViewer', [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView,
  Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer])

const documentLoad = function (args) {
  const viewer = pdfviewer.value.ej2Instances;
  viewer.addCustomMenu(this.menuItems, true);
}
import { PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
         TextSelection, TextSearch, Annotation, FormFields, FormDesigner,PageOrganizer } from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent, SwitchComponent } from "@syncfusion/ej2-vue-buttons";

export default {
    components: {
        'ejs-pdfviewer': PdfViewerComponent,
        'ejs-switch': SwitchComponent,
        'ejs-menu': MenuComponent,
        'ejs-checkbox': CheckBoxComponent
    },
    data: function() {
        return {
			documentPath:'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf',
            resourceUrl: 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib',
            menuItems : [
            {
                text: 'Search In Google',
                id: 'search_in_google',
                iconCss: 'e-icons e-search'
            },
            {
                text: 'Lock Annotation',
                iconCss: 'e-icons e-lock',
                id: 'lock_annotation'
            },
            {
                text: 'Unlock Annotation',
                iconCss: 'e-icons e-unlock',
                id: 'unlock_annotation'
            },
            {
                text: 'Lock Form Field',
                iconCss: 'e-icons e-lock',
                id: 'read_only_true'
            },
            {
                text: 'Unlock Form Field',
                iconCss: 'e-icons e-unlock',
                id: 'read_only_false'
            },
        ]
        }
    },
	provide: {
      PdfViewer: [ Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView,
                   Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner,PageOrganizer]
    },
    methods: {
    documentLoad: function (args) {
        var viewer = this.$refs.pdfviewer.ej2Instances;
        viewer.addCustomMenu(this.menuItems, true);
    },
  }
}

Show or hide custom items before opening

Use customContextMenuBeforeOpen() to hide or show custom options dynamically.

<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfviewer" :resourceUrl="resourceUrl" :documentPath="documentPath"
      :documentLoad="documentLoad" :customContextMenuBeforeOpen="customContextMenuBeforeOpen"
      :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>

<script>
import { PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer } from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";

export default {
  name: "App",
  components: {
    'ejs-pdfviewer': PdfViewerComponent,
    'ejs-menu': MenuComponent,
    'ejs-checkbox': CheckBoxComponent
  },
  data: function () {
    return {
      documentPath: 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf',
      resourceUrl: 'https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib',
      menuItems: [
        {
          text: 'Search In Google',
          id: 'search_in_google',
          iconCss: 'e-icons e-search'
        },
        {
          text: 'Lock Annotation',
          iconCss: 'e-icons e-lock',
          id: 'lock_annotation'
        },
        {
          text: 'Unlock Annotation',
          iconCss: 'e-icons e-unlock',
          id: 'unlock_annotation'
        },
        {
          text: 'Lock Form Field',
          iconCss: 'e-icons e-lock',
          id: 'read_only_true'
        },
        {
          text: 'Unlock Form Field',
          iconCss: 'e-icons e-unlock',
          id: 'read_only_false'
        },
      ]
    }
  },
  provide: {
    PdfViewer: [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
      TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]
  },
  methods: {
    documentLoad: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, false, false);
    },
    customContextMenuSelect: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      switch (args.id) {
        case 'search_in_google':
          for (let i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
            let content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
            if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
              window.open('http://google.com/search?q=' + content);
            }
          }
          break;
        case 'lock_annotation':
          this.lockAnnotations(args);
          break;
        case 'unlock_annotation':
          this.unlockAnnotations(args);
          break;
        case 'read_only_true':
          this.setReadOnlyTrue(args);
          break;
        case 'read_only_false':
          this.setReadOnlyFalse(args);
          break;
        default:
          break;
      }
    },

    customContextMenuBeforeOpen: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < args.ids.length; i++) {
        let search = document.getElementById(args.ids[i]);
        if (search) {
          search.style.display = 'none';
          if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
            search.style.display = 'block';
          } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
            let isLockOption = args.ids[i] === "lock_annotation";
            for (let j = 0; j < viewer.selectedItems.annotations.length; j++) {
              let selectedAnnotation = viewer.selectedItems.annotations[j];
              if (selectedAnnotation && selectedAnnotation.annotationSettings) {
                let shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
                  (!isLockOption && selectedAnnotation.annotationSettings.isLock);
                search.style.display = shouldDisplay ? 'block' : 'none';
              }
            }
          } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
            let isReadOnlyOption = args.ids[i] === "read_only_true";
            for (let j = 0; j < viewer.selectedItems.formFields.length; j++) {
              let selectedFormFields = viewer.selectedItems.formFields[j];
              if (selectedFormFields) {
                let selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
                let displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
                search.style.display = displayMenu ? 'block' : 'none';
              }
            }
          } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
            search.style.display = 'block';
          }
        }
      }
    },

    lockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = true;
          viewer.annotationCollection[i].isCommentLock = true;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    unlockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = false;
          viewer.annotationCollection[i].isCommentLock = false;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    setReadOnlyTrue: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: true,
          });
        }
        args.cancel = false;
      }
    },

    setReadOnlyFalse: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: false,
          });
        }
        args.cancel = false;
      }
    },

    contextmenuHelper: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, enable.checked, position.checked);
    },
  }
};
</script>
<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfviewer" :serviceUrl="serviceUrl" :documentPath="documentPath"
      :documentLoad="documentLoad" :customContextMenuBeforeOpen="customContextMenuBeforeOpen"
      :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>
<script setup>
import {
  PdfViewerComponent as EjsPdfviewer, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer
} from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent as EjsMenu } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent as EjsCheckbox } from "@syncfusion/ej2-vue-buttons";
import { provide, ref } from "vue";

const pdfviewer = ref(null);
const documentPath = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';
const serviceUrl = "https://document.syncfusion.com/web-services/pdf-viewer/api/pdfviewer";
const menuItems = [
  {
    text: 'Search In Google',
    id: 'search_in_google',
    iconCss: 'e-icons e-search'
  },
  {
    text: 'Lock Annotation',
    iconCss: 'e-icons e-lock',
    id: 'lock_annotation'
  },
  {
    text: 'Unlock Annotation',
    iconCss: 'e-icons e-unlock',
    id: 'unlock_annotation'
  },
  {
    text: 'Lock Form Field',
    iconCss: 'e-icons e-lock',
    id: 'read_only_true'
  },
  {
    text: 'Unlock Form Field',
    iconCss: 'e-icons e-unlock',
    id: 'read_only_false'
  },
]

provide('PdfViewer', [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer])

const documentLoad = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  viewer.addCustomMenu(menuItems, false, false);
}
const customContextMenuSelect = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  switch (args.id) {
    case 'search_in_google':
      for (let i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
        let content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
        if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
          window.open('http://google.com/search?q=' + content);
        }
      }
      break;
    case 'lock_annotation':
      lockAnnotations(args);
      break;
    case 'unlock_annotation':
      unlockAnnotations(args);
      break;
    case 'read_only_true':
      setReadOnlyTrue(args);
      break;
    case 'read_only_false':
      setReadOnlyFalse(args);
      break;
    default:
      break;
  }
}

const customContextMenuBeforeOpen = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  for (let i = 0; i < args.ids.length; i++) {
    let search = document.getElementById(args.ids[i]);
    if (search) {
      search.style.display = 'none';
      if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
        search.style.display = 'block';
      } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
        let isLockOption = args.ids[i] === "lock_annotation";
        for (let j = 0; j < viewer.selectedItems.annotations.length; j++) {
          let selectedAnnotation = viewer.selectedItems.annotations[j];
          if (selectedAnnotation && selectedAnnotation.annotationSettings) {
            let shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
              (!isLockOption && selectedAnnotation.annotationSettings.isLock);
            search.style.display = shouldDisplay ? 'block' : 'none';
          }
        }
      } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
        let isReadOnlyOption = args.ids[i] === "read_only_true";
        for (let j = 0; j < viewer.selectedItems.formFields.length; j++) {
          let selectedFormFields = viewer.selectedItems.formFields[j];
          if (selectedFormFields) {
            let selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
            let displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
            search.style.display = displayMenu ? 'block' : 'none';
          }
        }
      } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
        search.style.display = 'block';
      }
    }
  }
}

const lockAnnotations = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  for (let i = 0; i < viewer.annotationCollection.length; i++) {
    if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
      viewer.annotationCollection[i].annotationSettings.isLock = true;
      viewer.annotationCollection[i].isCommentLock = true;
      viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
    }
    args.cancel = false;
  }
}

const unlockAnnotations = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  for (let i = 0; i < viewer.annotationCollection.length; i++) {
    if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
      viewer.annotationCollection[i].annotationSettings.isLock = false;
      viewer.annotationCollection[i].isCommentLock = false;
      viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
    }
    args.cancel = false;
  }
}

const setReadOnlyTrue = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  let selectedFormFields = viewer.selectedItems.formFields;
  for (let i = 0; i < selectedFormFields.length; i++) {
    let selectedFormField = selectedFormFields[i];
    if (selectedFormField) {
      viewer.formDesignerModule.updateFormField(selectedFormField, {
        isReadOnly: true,
      });
    }
    args.cancel = false;
  }
}

const setReadOnlyFalse = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  let selectedFormFields = viewer.selectedItems.formFields;
  for (let i = 0; i < selectedFormFields.length; i++) {
    let selectedFormField = selectedFormFields[i];
    if (selectedFormField) {
      viewer.formDesignerModule.updateFormField(selectedFormField, {
        isReadOnly: false,
      });
    }
    args.cancel = false;
  }
}

const contextmenuHelper = function (args) {
  let viewer = pdfviewer.value.ej2Instances;
  viewer.addCustomMenu(menuItems, enable.checked, position.checked);
}

</script>
<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfviewer" :serviceUrl="serviceUrl" :documentPath="documentPath"
      :documentLoad="documentLoad" :customContextMenuBeforeOpen="customContextMenuBeforeOpen"
      :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>
<script>
import {
  PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
  TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer
} from "@syncfusion/ej2-vue-pdfviewer";
import { MenuComponent } from "@syncfusion/ej2-vue-navigations";
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";

export default {
  name: "App",
  components: {
    'ejs-pdfviewer': PdfViewerComponent,
    'ejs-menu': MenuComponent,
    'ejs-checkbox': CheckBoxComponent
  },
  data: function () {
    return {
      documentPath: 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf',
      serviceUrl: "https://document.syncfusion.com/web-services/pdf-viewer/api/pdfviewer",
      menuItems: [
        {
          text: 'Search In Google',
          id: 'search_in_google',
          iconCss: 'e-icons e-search'
        },
        {
          text: 'Lock Annotation',
          iconCss: 'e-icons e-lock',
          id: 'lock_annotation'
        },
        {
          text: 'Unlock Annotation',
          iconCss: 'e-icons e-unlock',
          id: 'unlock_annotation'
        },
        {
          text: 'Lock Form Field',
          iconCss: 'e-icons e-lock',
          id: 'read_only_true'
        },
        {
          text: 'Unlock Form Field',
          iconCss: 'e-icons e-unlock',
          id: 'read_only_false'
        },
      ]
    }
  },
  provide: {
    PdfViewer: [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print,
      TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]
  },
  methods: {
    documentLoad: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, false, false);
    },
    customContextMenuSelect: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      switch (args.id) {
        case 'search_in_google':
          for (let i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
            let content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
            if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
              window.open('http://google.com/search?q=' + content);
            }
          }
          break;
        case 'lock_annotation':
          this.lockAnnotations(args);
          break;
        case 'unlock_annotation':
          this.unlockAnnotations(args);
          break;
        case 'read_only_true':
          this.setReadOnlyTrue(args);
          break;
        case 'read_only_false':
          this.setReadOnlyFalse(args);
          break;
        default:
          break;
      }
    },

    customContextMenuBeforeOpen: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < args.ids.length; i++) {
        let search = document.getElementById(args.ids[i]);
        if (search) {
          search.style.display = 'none';
          if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
            search.style.display = 'block';
          } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
            let isLockOption = args.ids[i] === "lock_annotation";
            for (let j = 0; j < viewer.selectedItems.annotations.length; j++) {
              let selectedAnnotation = viewer.selectedItems.annotations[j];
              if (selectedAnnotation && selectedAnnotation.annotationSettings) {
                let shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
                  (!isLockOption && selectedAnnotation.annotationSettings.isLock);
                search.style.display = shouldDisplay ? 'block' : 'none';
              }
            }
          } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
            let isReadOnlyOption = args.ids[i] === "read_only_true";
            for (let j = 0; j < viewer.selectedItems.formFields.length; j++) {
              let selectedFormFields = viewer.selectedItems.formFields[j];
              if (selectedFormFields) {
                let selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
                let displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
                search.style.display = displayMenu ? 'block' : 'none';
              }
            }
          } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
            search.style.display = 'block';
          }
        }
      }
    },

    lockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = true;
          viewer.annotationCollection[i].isCommentLock = true;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    unlockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = false;
          viewer.annotationCollection[i].isCommentLock = false;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    setReadOnlyTrue: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: true,
          });
        }
        args.cancel = false;
      }
    },

    setReadOnlyFalse: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: false,
          });
        }
        args.cancel = false;
      }
    },

    contextmenuHelper: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, enable.checked, position.checked);
    },
  }
};
</script>

The following is the output of custom context menu with customization.

<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfViewer" :resourceUrl="resourceUrl" :documentPath="documentPath"
      :documentLoad="documentLoad" :customContextMenuBeforeOpen="customContextMenuBeforeOpen"
      :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>

<script setup>
import { provide, ref } from "vue";
import {
  PdfViewerComponent as EjsPdfviewer, Toolbar, Magnification, Navigation, LinkAnnotation,
  BookmarkView, ThumbnailView, Print, TextSelection, TextSearch,
  Annotation, FormDesigner, FormFields, PageOrganizer
} from '@syncfusion/ej2-vue-pdfviewer';
import { CheckBoxComponent as EjsCheckbox } from "@syncfusion/ej2-vue-buttons";

const pdfViewer = ref(null);
const documentPath = "https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf";
const resourceUrl = "https://cdn.syncfusion.com/ej2/25.1.35/dist/ej2-pdfviewer-lib";
const menuItems = ref([{
  text: 'Search In Google',
  id: 'search_in_google',
  iconCss: 'e-icons e-search'
},
{
  text: 'Lock Annotation',
  iconCss: 'e-icons e-lock',
  id: 'lock_annotation'
},
{
  text: 'Unlock Annotation',
  iconCss: 'e-icons e-unlock',
  id: 'unlock_annotation'
},
{
  text: 'Lock Form Fields',
  iconCss: 'e-icons e-lock',
  id: 'read_only_true'
},
{
  text: 'Unlock Form Fields',
  iconCss: 'e-icons e-unlock',
  id: 'read_only_false'
},
]);

provide('PdfViewer', [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView,
  Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]);

const documentLoad = function () {
  const viewer = pdfViewer.value.ej2Instances;
  viewer.addCustomMenu(menuItems.value, false, false);
}
const customContextMenuSelect = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  switch (args.id) {
    case 'search_in_google':
      for (var i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
        var content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
        if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
          window.open('http://google.com/search?q=' + content);
        }
      }
      break;
    case 'lock_annotation':
      lockAnnotations(args);
      break;
    case 'unlock_annotation':
      unlockAnnotations(args);
      break;
    case 'read_only_true':
      setReadOnlyTrue(args);
      break;
    case 'read_only_false':
      setReadOnlyFalse(args);
      break;
    default:
      break;
  }
}

const customContextMenuBeforeOpen = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  for (var i = 0; i < args.ids.length; i++) {
    var search = document.getElementById(args.ids[i]);
    if (search) {
      search.style.display = 'none';
      if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
        search.style.display = 'block';
      } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
        var isLockOption = args.ids[i] === "lock_annotation";
        for (var j = 0; j < viewer.selectedItems.annotations.length; j++) {
          var selectedAnnotation = viewer.selectedItems.annotations[j];
          if (selectedAnnotation && selectedAnnotation.annotationSettings) {
            var shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
              (!isLockOption && selectedAnnotation.annotationSettings.isLock);
            search.style.display = shouldDisplay ? 'block' : 'none';
          }
        }
      } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
        var isReadOnlyOption = args.ids[i] === "read_only_true";
        for (var j = 0; j < viewer.selectedItems.formFields.length; j++) {
          var selectedFormFields = viewer.selectedItems.formFields[j];
          if (selectedFormFields) {
            var selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
            var displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
            search.style.display = displayMenu ? 'block' : 'none';
          }
        }
      } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
        search.style.display = 'block';
      }
    }
  }
}

const lockAnnotations = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  for (var i = 0; i < viewer.annotationCollection.length; i++) {
    if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
      viewer.annotationCollection[i].annotationSettings.isLock = true;
      viewer.annotationCollection[i].isCommentLock = true;
      viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
    }
    args.cancel = false;
  }
}

const unlockAnnotations = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  for (var i = 0; i < viewer.annotationCollection.length; i++) {
    if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
      viewer.annotationCollection[i].annotationSettings.isLock = false;
      viewer.annotationCollection[i].isCommentLock = false;
      viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
    }
    args.cancel = false;
  }
}

const setReadOnlyTrue = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  var selectedFormFields = viewer.selectedItems.formFields;
  for (var i = 0; i < selectedFormFields.length; i++) {
    var selectedFormField = selectedFormFields[i];
    if (selectedFormField) {
      viewer.formDesignerModule.updateFormField(selectedFormField, {
        isReadOnly: true,
      });
    }
    args.cancel = false;
  }
}

const setReadOnlyFalse = function (args) {
  const viewer = pdfViewer.value.ej2Instances;
  var selectedFormFields = viewer.selectedItems.formFields;
  for (var i = 0; i < selectedFormFields.length; i++) {
    var selectedFormField = selectedFormFields[i];
    if (selectedFormField) {
      viewer.formDesignerModule.updateFormField(selectedFormField, {
        isReadOnly: false,
      });
    }
    args.cancel = false;
  }
}

const contextmenuHelper = function () {
  const viewer = pdfViewer.value.ej2Instances;
  viewer.addCustomMenu(menuItems.value, enable.checked, position.checked);
}
</script>
<style>
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/material.css';
@import '../node_modules/@syncfusion/ej2-vue-pdfviewer/styles/material.css';

#pdfViewer {
  height: 640px;
}
</style>
<template>
  <div id="app">
    <ul>
      <ejs-checkbox label='Hide Default Context Menu' id="enable" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
      <ejs-checkbox label='Add Custom option at bottom' id="position" @change="contextmenuHelper"
        checked="false"></ejs-checkbox>
    </ul>
    <ejs-pdfviewer id="pdfViewer" ref="pdfviewer":resourceUrl="resourceUrl" :documentPath="documentPath" :documentLoad="documentLoad"
      :customContextMenuBeforeOpen="customContextMenuBeforeOpen" :customContextMenuSelect="customContextMenuSelect">
    </ejs-pdfviewer>
  </div>
</template>

<script>
import {
  PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation,
  BookmarkView, ThumbnailView, Print, TextSelection, TextSearch,
  Annotation, FormDesigner, FormFields, PageOrganizer
} from '@syncfusion/ej2-vue-pdfviewer';
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";

export default {
  name: "App",
  components: {
    'ejs-pdfviewer': PdfViewerComponent,
    'ejs-checkbox': CheckBoxComponent
  },
  name: 'app',
  data() {
    return {
      documentPath: "https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf",
      resourceUrl: "https://cdn.syncfusion.com/ej2/25.1.35/dist/ej2-pdfviewer-lib",
      menuItems: [{
        text: 'Search In Google',
        id: 'search_in_google',
        iconCss: 'e-icons e-search'
      },
      {
        text: 'Lock Annotation',
        iconCss: 'e-icons e-lock',
        id: 'lock_annotation'
      },
      {
        text: 'Unlock Annotation',
        iconCss: 'e-icons e-unlock',
        id: 'unlock_annotation'
      },
      {
        text: 'Lock Form Fields',
        iconCss: 'e-icons e-lock',
        id: 'read_only_true'
      },
      {
        text: 'Unlock Form Fields',
        iconCss: 'e-icons e-unlock',
        id: 'read_only_false'
      },
      ]
    };
  },

  provide: {
    PdfViewer: [Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView,
      Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]
  },
  methods: {
    documentLoad: function () {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, false, false);
    },
    customContextMenuSelect: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      switch (args.id) {
        case 'search_in_google':
          for (let i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
            let content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
            if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
              window.open('http://google.com/search?q=' + content);
            }
          }
          break;
        case 'lock_annotation':
          this.lockAnnotations(args);
          break;
        case 'unlock_annotation':
          this.unlockAnnotations(args);
          break;
        case 'read_only_true':
          this.setReadOnlyTrue(args);
          break;
        case 'read_only_false':
          this.setReadOnlyFalse(args);
          break;
        default:
          break;
      }
    },

    customContextMenuBeforeOpen: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < args.ids.length; i++) {
        let search = document.getElementById(args.ids[i]);
        if (search) {
          search.style.display = 'none';
          if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
            search.style.display = 'block';
          } else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
            let isLockOption = args.ids[i] === "lock_annotation";
            for (let j = 0; j < viewer.selectedItems.annotations.length; j++) {
              let selectedAnnotation = viewer.selectedItems.annotations[j];
              if (selectedAnnotation && selectedAnnotation.annotationSettings) {
                let shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
                  (!isLockOption && selectedAnnotation.annotationSettings.isLock);
                search.style.display = shouldDisplay ? 'block' : 'none';
              }
            }
          } else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
            let isReadOnlyOption = args.ids[i] === "read_only_true";
            for (let j = 0; j < viewer.selectedItems.formFields.length; j++) {
              let selectedFormFields = viewer.selectedItems.formFields[j];
              if (selectedFormFields) {
                let selectedFormField = viewer.selectedItems.formFields[j].isReadonly;
                let displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
                search.style.display = displayMenu ? 'block' : 'none';
              }
            }
          } else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
            search.style.display = 'block';
          }
        }
      }
    },

    lockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = true;
          viewer.annotationCollection[i].isCommentLock = true;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    unlockAnnotations: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      for (let i = 0; i < viewer.annotationCollection.length; i++) {
        if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
          viewer.annotationCollection[i].annotationSettings.isLock = false;
          viewer.annotationCollection[i].isCommentLock = false;
          viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
        }
        args.cancel = false;
      }
    },

    setReadOnlyTrue: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: true,
          });
        }
        args.cancel = false;
      }
    },

    setReadOnlyFalse: function (args) {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      let selectedFormFields = viewer.selectedItems.formFields;
      for (let i = 0; i < selectedFormFields.length; i++) {
        let selectedFormField = selectedFormFields[i];
        if (selectedFormField) {
          viewer.formDesignerModule.updateFormField(selectedFormField, {
            isReadOnly: false,
          });
        }
        args.cancel = false;
      }
    },

    contextmenuHelper: function () {
      let viewer = this.$refs.pdfviewer.ej2Instances;
      viewer.addCustomMenu(this.menuItems, enable.checked, position.checked);
    },
  }
}
</script>
<style>
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/material.css';
@import '../node_modules/@syncfusion/ej2-vue-pdfviewer/styles/material.css';

#pdfViewer {
  height: 640px;
}
</style>

To set up the server-backed PDF Viewer, add the following service URL in App.vue:
serviceUrl: 'https://document.syncfusion.com/web-services/pdf-viewer/api/pdfviewer'
Then configure the PDF Viewer template with the :serviceUrl='service' attribute on the viewer container.

View sample in GitHub