Your Menus, Your Rules.

Tired of the boring default right-click menu? QuickCTX is your zero-dependency key to unlocking beautiful, fully-customizable context menus that behave exactly how you want.

A right-click on the box will reveal the magic.

const heroMenu = new QuickCTX();
ctxManager.createAndBindMenu({
    menuId: "hero",
    selector: "#hero-box",
    structure: [
        { label: "Say Hello", action: () => showToast("Hello! 👾"), icon: "fas fa-hand" },
        { label: "✨ Magic Trick", action: launchConfetti },
        MenuCommand.Separator(),
        { label: "Get Started", action: () => goToSection("#submenus"), icon: "fas fa-rocket" },
    ]
});

Right-Click Here

A simple menu will appear.

Not Just for Context.

Go beyond the right-click. With custom triggers, QuickCTX becomes a versatile UI tool. Bind menus to a click, a hover, or a double-click, to create dynamic toolbars, interactive info boxes, or user-activated dropdowns with the same simple API. All options are reactive and can be updated at runtime.

Play with the controls below. Watch the code and the box's behavior change instantly.

Trigger:

ctxManager.updateMenuConfiguration("trigger", {triggerEvent: selectedTrigger})

ctxManager.updateOptions({animations: {hoverMenuOpenDelay: currentDelay, hoverMenuCloseDelay: currentDelay}})

Dynamic Trigger

Interact with me!

The Right Tool, at the Right Time.

A truly smart UI only shows what's relevant. Use targetTypes to create a single menu configuration that intelligently adapts, showing or disabling commands based on the context.

The menu knows what you click. Try the folder, then the image.

ctxManager.createAndBindMenu({
menuId: "contextual",
selector: ".context-item",
filterStrategy: "disable",
structure: [
    { label: "Open", action: "open_folder", icon: "fas fa-folder-open", targetTypes: ["folder"] },
    { label: "Edit Image", action: "edit_image", icon: "fas fa-paint-brush", targetTypes: ["image"] }, ...
});
Project
cat.jpg

Precision in Depth.

Overlapping clickable areas can be tricky. With overlapStrategy, you decide what happens. Target the most specific element with 'deepest' (default), or the parent container with 'closest'. You are in full control.

The inner box has its own menu. Right-click it to see the 'deepest' strategy at work.

const ctxManager = new QuickCTX({
  overlapStrategy: 'deepest'
});

ctxManager.createAndBindMenu({
    menuId: 'menu1',
    selector: '#nesting-box-outer',
    structure: [{ label: 'Outer Action' }]
});

ctxManager.createAndBindMenu({
    menuId: 'menu2',
    selector: '#nesting-box-inner',
    structure: [{ label: 'Inner Action' }]
});

Outer Box

Inner Box

Style it Your Way.

Make the menu a true part of your design. Override CSS variables for quick theming that matches your brand, or use custom classes for pixel-perfect control over each part of your menu.

The menus you've seen all over this page is already adapted to the theme, actually. However, this box has a little different themed menu...

// CSS
.quickctx-container {
  --quickctx-shadow: 10px 10px 0px rgba(0, 0, 0, var(--c-bg));
  /* ...and so on */
}

.quickctx-container.style-custom {
    --quickctx-border-color: var(--c-card-border);
    --quickctx-border-size: 2px;
    --quickctx-border-radius: 4px;
    --quickctx-font-family: var(--font-mono);
    /* ... other declarations... */
}

// JS
const ctxManager = new QuickCTX({
    overlapStrategy: "deepest",
    classes: {
        container: "quickctx-container pointable hide-outline-on-hover",
        item: "quickctx-item transition-all",
    },
});

//...

ctxManager.createAndBindMenu({
    menuId: "styleCustom",
    selector: "#style-box-custom",
    additionalClasses: "style-custom",
    //...
});

Custom Class

Unique flair

Keep Your Logic Clean.

Prefer a centralized event bus over callbacks? QuickCTX emits a global QuickCTXActionSelected event. Listen for it anywhere in your app to centralize your action handling.

Click an action. The toast you see is triggered by a single, global event listener.

document.addEventListener('QuickCTXActionSelected', e => {
  const { commandId, commandLabel, targetElement, targetType, menuId } = e.detail;

  console.log(`Action "${commandLabel}" triggered!`);
  
  // Example: Show a notification
  showToast(`Event caught! Action label: '${commandLabel}'`);

  // Your application logic here...
  if (action === 'doSomething') {
    doSomething(targetElement);
  }
});

Catch My Events

Actions trigger global events.

Code Smarter, Not Harder.

Write less, achieve more. Define common action sets and reuse them across multiple menus with the spread operator. And when you need to peek under the hood, enable the built-in logger.

This menu is built by reusing a common set of actions, plus a few unique ones.

const commonActions = [
  { label: 'Copy', icon: 'copy' },
  { label: 'Paste', icon: 'paste', disabled: true },
];

ctxManager.createAndBindMenu({
    menuId: "advanced",
    selector: "#advanced-box",
    structure: [
        ...commonActions,
        MenuCommand.Separator(),
        { label: "Lil joke :)", action: (e) => jumpscarehehe(e), iconClass: "fas fa-ghost" },
        { label: "Show Cat", action: "toggleNyanCat", iconClass: "fas fa-cat" }
    ],
});

// See the console for debug logs
QuickCTX.setLoggerIsEnabled(true);

Code Smarter

Reuse and debug.

Ready to build?

QuickCTX is open-source and ready for your next project.

Get it on GitHub

I built QuickCTX in my free time, fueled by passion and coffee. If you find it useful, consider supporting the project. It helps me dedicate more time to projects like this one. Every little bit makes a huge difference. Thank you! 💙

For a comprehensive guide to all options, methods, and advanced features, please check out the full README on GitHub.