Typed context menu builder with nested menus and fluent API for Chrome extensions. Part of @zovo/webext.
- Fluent Builder API โ Chain methods intuitively for readable menu definitions
- Nested Submenus โ Create unlimited depth menu hierarchies with ease
- Context-Aware Items โ Show menus only for page, selection, link, image, or other contexts
- Separators โ Visually group related menu items
- Radio & Checkbox Items โ Create selectable options with proper state management
- Dynamic Updates โ Modify menu title, visibility, enabled state at runtime
- Typed Click Handlers โ Full TypeScript types for click event info
npm install @theluckystrike/webext-context-menuCreate a context menu that appears when text is selected:
import { createMenu, registerMenus } from "@theluckystrike/webext-context-menu";
const searchItem = createMenu(
{ id: "search", title: "Search '%s'", contexts: ["selection"] },
(info) => {
const query = encodeURIComponent(info.selectionText || "");
window.open(`https://google.com/search?q=${query}`);
}
);
registerMenus([searchItem]);Build nested menus using the fluent API:
import { createMenu, createSeparator, registerMenus } from "@theluckystrike/webext-context-menu";
const toolsMenu = createMenu({ id: "tools", title: "Developer Tools", contexts: ["page"] });
toolsMenu
.addChild(
createMenu({ id: "copy-url", title: "Copy Page URL" }, (info) => {
navigator.clipboard.writeText(info.pageUrl || "");
})
)
.addChild(createSeparator("sep1"))
.addChild(
createMenu({ id: "view-source", title: "View Source" }, (info) => {
window.open(`${info.pageUrl}?view-source`);
})
);
registerMenus([toolsMenu]);The fluent builder API lets you construct complex multi-level menus:
import { createMenu, createSeparator, registerMenus } from "@theluckystrike/webext-context-menu";
const rootMenu = createMenu({ id: "root", title: "๐ ๏ธ Utilities", contexts: ["all"] });
const sharing = createMenu({ id: "sharing", title: "Share" });
const editing = createMenu({ id: "editing", title: "Edit" });
sharing
.addChild(createMenu({ id: "share-twitter", title: "Twitter", contexts: ["page"] }, (info) => {
window.open(`https://twitter.com/intent/tweet?url=${info.pageUrl}`);
}))
.addChild(createMenu({ id: "share-email", title: "Email", contexts: ["page"] }, (info) => {
window.open(`mailto:?body=${info.pageUrl}`);
}));
const formatMenu = createMenu({ id: "format", title: "Format" });
formatMenu
.addChild(createMenu({ id: "fmt-bold", title: "Bold", type: "radio", checked: true }, () => {}))
.addChild(createMenu({ id: "fmt-italic", title: "Italic", type: "radio" }, () => {}));
editing.addChild(formatMenu);
rootMenu.addChildren([sharing, editing]);
registerMenus([rootMenu]);The library supports all Chrome context menu contexts:
createMenu({ id: "page-info", title: "Page Info", contexts: ["page"] }, (info) => {
console.log("Page URL:", info.pageUrl);
});createMenu({ id: "search-selection", title: "Search '%s'", contexts: ["selection"] }, (info) => {
console.log("Selected:", info.selectionText);
});createMenu({ id: "copy-link", title: "Copy Link", contexts: ["link"] }, (info) => {
navigator.clipboard.writeText(info.linkUrl || "");
});createMenu({ id: "image-search", title: "Search Image", contexts: ["image"] }, (info) => {
window.open(`https://lens.google.com/uploadbyurl?url=${info.srcUrl}`);
});Update or remove menu items at runtime:
import { updateMenu, removeMenu, removeAllMenus } from "@theluckystrike/webext-context-menu";
await updateMenu("search", { enabled: false });
await updateMenu("option-1", { checked: true });
await removeMenu("old-feature");
await removeAllMenus();Creates a menu item. Returns a MenuItem for chaining.
| Option | Type | Default | Description |
|---|---|---|---|
id |
string |
โ | Unique identifier (required) |
title |
string |
โ | Display text. Use %s for selected text |
contexts |
ContextType[] |
["all"] |
Contexts where menu appears |
type |
"normal" | "checkbox" | "radio" | "separator" |
"normal" |
Menu item type |
parentId |
string |
โ | Parent menu ID |
enabled |
boolean |
true |
Whether item is clickable |
visible |
boolean |
true |
Whether item is visible |
checked |
boolean |
false |
Checked state (checkbox/radio) |
Creates a separator line for visual grouping.
Adds child items to create nested menus. Returns this for chaining.
Registers all menu items with Chrome. Sets up click listener automatically.
Updates a menu item. Available: title, enabled, visible, checked.
Removes a single menu or all menus.
Add contextMenus to your manifest.json:
{
"manifest_version": 3,
"permissions": ["contextMenus"]
}MIT
Built by theluckystrike โ zovo.one