portal-vue
A lightweight Vue 3 plugin for rendering component content anywhere in the DOM, independent of the component tree.
Repository Health
Technical Analysis
portal-vue is a small Vue 3 plugin that lets a component render its slot content into a completely different part of the DOM — anywhere in the document — while keeping the component’s reactivity, lifecycle, and prop bindings intact. It ships two components, <portal> and <portal-target>, matched by name, plus a useWormhole composable for programmatic access to the same transport registry.
Under the hood it uses a reactive registry (the ‘wormhole’) built on Vue’s own reactivity primitives, so content moves between named targets without manual DOM manipulation, document.body refs, or teleport-target boilerplate. It’s commonly reached for modals, dropdowns, notifications, and any UI that needs to escape overflow/z-index constraints imposed by a parent component.
What You Get
- A
<portal>component that sends its default slot to a named<portal-target>anywhere in the app - A
<portal-target>component that renders content sent to its name, with optionalmultiplesupport for several senders - A
useWormhole/provideWormholecomposable pair for programmatic access to the same transport registry outside components - Full TypeScript types for props, transports, and the wormhole API, plus dual ESM/UMD build outputs
Common Use Cases
- Modal dialogs and drawers that need to render outside a scrollable or overflow:hidden parent
- Global toast/notification systems fed from deeply nested components
- Dropdown/tooltip menus that must escape a parent’s stacking context or transform
- Multi-target dashboards where several components broadcast content into one shared slot, ordered via the order prop
Under The Hood
Architecture
The library separates state from presentation cleanly: wormhole.ts implements a reactive registry (createWormhole) built on Vue’s reactive/readonly primitives, tracking a Map<target, Map<sender, Transport>> of open transports; composables/wormhole.ts exposes that registry to the component tree via provide/inject (useWormhole/provideWormhole); and components/portal.ts and components/portal-target.ts are thin defineComponent wrappers that call open/close/getContentForTarget on lifecycle hooks (onMounted, onUpdated, onBeforeUnmount) and prop watchers. This keeps the transport logic fully decoupled from rendering, so useWormhole can be used standalone without either component.
Tech Stack
Written in TypeScript against Vue 3 (peer dependency ^3.0.4), built with Vite in two passes (vite.config.ts for the production ESM/UMD bundles, vite.config.dev.ts for dev-mode builds with warnings retained), with vue-tsc generating .d.ts declarations separately from the JS build. Documentation is authored in VitePress. Linting runs through a shared @linusborg/eslint-config plus Prettier, enforced pre-commit via yorkie and lint-staged. Package management and scripts use pnpm.
Code Quality
Tests are comprehensive for the library’s size: wormhole.spec.ts, the-portal.spec.ts, and portal-target.spec.ts unit-test each piece in isolation, while integration.spec.ts mounts real scenario components (found under __tests__/resources/) with @vue/test-utils and asserts against snapshots. Types are used throughout (types.ts defines Transport, Wormhole, PortalProps, etc.) rather than treating props as loosely-typed objects. Error handling is minimal but intentional — useWormhole throws a clear message if the plugin wasn’t installed, and assertStaticProps emits dev-only warnings rather than throwing when a documented-static prop changes. A GitHub Actions workflow runs the test suite on push.
API Design
The public surface is deliberately small: install the plugin once, then use two components and, optionally, one composable — no context providers or extra setup needed beyond app.use(PortalVue). Naming mirrors the underlying concept directly (to/name on portal and target), and the order/multiple props extend the basic single-sender case without complicating it. The main friction point is conceptual overlap with Vue 3’s native <Teleport>, which now covers much of the same ground for simple cases, leaving portal-vue’s value concentrated in named multi-target broadcasting and the change event for wrapper-side reactivity.
Used by 2 apps in this directory
GitLab
Devops · Developer Tools
The complete DevOps platform that unifies Git hosting, CI/CD, issue tracking, and security scanning into a single self-hostable application.
Uptrace
Monitoring · Devops
Unified open-source APM that collects OpenTelemetry traces, metrics, and logs into a single self-hosted platform backed by ClickHouse.