A Vue 3 + Vue Router 4 package that adds intelligent navigation behavior, specifically Cmd/Ctrl+click functionality to programmatic router.push() calls.
- π― Smart Navigation: Automatically detects Cmd/Ctrl key presses during router.push()calls
- π New Tab Support: Opens routes in new tabs when modifier keys are pressed
- π§ Highly Configurable: Custom behaviors, callbacks, and route patterns
- π± Cross-Platform: Works on macOS (Cmd), Windows/Linux (Ctrl)
- π‘οΈ Type Safe: Full TypeScript support with comprehensive type definitions
- π Zero Breaking Changes: Non-intrusive addition of new tab functionality to existing router
- π¦ Tree Shakeable: Optimized bundle size with ESM support
- π§ͺ Well Tested: Comprehensive test coverage with Vitest and Playwright
npm install vue-router-newtab
# or
yarn add vue-router-newtab
# or
pnpm add vue-router-newtabimport { createRouter, createWebHistory } from 'vue-router';
import { newTabRouter } from 'vue-router-newtab';
const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About },
    { path: '/contact', component: Contact },
  ],
});
// Enhance the router with Cmd/Ctrl+click support
newTabRouter(router);
// Now your existing router.push() calls work intelligently!
// Cmd/Ctrl + router.push('/about') = opens in new tab
// Normal router.push('/about') = normal navigationIf you're using unplugin-vue-router for file-based routing, you need to import the type declarations in your main setup file:
// main.ts or router setup file
import 'vue-router-newtab/unplugin-vue-router.d.ts';
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import { newTabRouter } from 'vue-router-newtab';
// Your router setup with unplugin-vue-router
const router = createRouter({
  history: createWebHistory(),
  routes: [], // Routes are auto-generated by unplugin-vue-router
});
// Enhance the router with Cmd/Ctrl+click support
newTabRouter(router);This import ensures that TypeScript recognizes the enhanced router.push() method with the additional options when using unplugin-vue-router's typed routing.
<template>
  <div>
    <button @click="navigateToAbout">About (Cmd/Ctrl+click for new tab)</button>
    <button @click="forceNewTab">Force New Tab</button>
  </div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router';
const router = useRouter();
const navigateToAbout = () => {
  // This will open in new tab if Cmd/Ctrl is pressed
  router.push('/about');
};
const forceNewTab = () => {
  // This will always open in new tab
  router.push('/about', { forceNewTab: true });
};
const conditionalNewTab = () => {
  // This will only open in new tab if Cmd/Ctrl is pressed
  router.push('/about', { newTab: true });
};
</script>Adds new tab functionality to a Vue Router instance with intelligent navigation behavior.
Parameters:
- router: Router- The Vue Router instance to support newTab behaviour
- config?: NewTabRouterConfig- Optional configuration object
Returns: NewTabRouter - The router instance with new tab functionality
interface NewTabRouterConfig {
  /** Enable Cmd/Ctrl+click detection (default: true) */
  enableCtrlClick?: boolean;
  /** Enable debug logging (default: false) */
  debugMode?: boolean;
}interface EnhancedPushOptions {
  /** Force opening in new tab regardless of modifier key */
  forceNewTab?: boolean;
  /** Open in new tab only if modifier key is pressed (false prevents new tab behavior) */
  newTab?: boolean;
}newTabRouter(router, {
  debugMode: process.env.NODE_ENV === 'development',
});// Force new tab for specific calls
router.push('/external-link', { forceNewTab: true });
// Conditional new tab (only if modifier key is pressed)
router.push('/optional-new-tab', { newTab: true });
// Prevent new tab behavior for specific calls
router.push('/normal-navigation', { newTab: false });The package provides comprehensive TypeScript support with full type definitions:
import type {
  NewTabRouterConfig,
  EnhancedPushOptions,
  NewTabRouter,
} from 'vue-router-newtab';
// Your router is now fully typed
const router: NewTabRouter = newTabRouter(
  createRouter({
    /* ... */
  })
);
// newTab push method with full type safety
router.push('/about', { forceNewTab: true });npm run testnpm run test:coveragenpm run test:e2e- β Chrome 88+
- β Firefox 85+
- β Safari 14+
- β Edge 88+
- ESM: ~3.2KB gzipped
- CJS: ~3.5KB gzipped
The new tab router automatically detects server-side environments and skips new tab functionality to prevent SSR errors:
// Safe to use in SSR - will be skipped automatically
newTabRouter(router);The new tab router automatically cleans up event listeners when the router is destroyed. For manual cleanup:
import { destroyNewTabRouter } from 'vue-router-newtab';
// Clean up when needed
destroyNewTabRouter(router);- New tabs are opened with noopener,noreferrerfor security
- External URLs are detected and handled appropriately
- Popup blockers are handled gracefully with fallback to normal navigation
The new tab router is designed to be non-intrusive. Your existing code will work without changes:
// Before
router.push('/about');
// After (same code, enhanced behavior)
newTabRouter(router);
router.push('/about'); // Now supports Cmd/Ctrl+click!Contributions are welcome! Please read our Contributing Guide for details.
# Clone the repository
git clone https://github.com/sjmc11/vue-router-newtab.git
# Install dependencies
npm install
# Run tests
npm run test
# Build the package
npm run buildThis project is licensed under the MIT License - see the LICENSE file for details.
- Vue.js team for the amazing framework
- Vue Router team for the excellent routing solution
- All contributors who help make this project better
- π Documentation
- π Issue Tracker
- π¬ Discussions
Made with β€οΈ for the Vue.js community