PortalVue for Vue 3
PortalVue is a Vue 3 component library that enables developers to render DOM elements outside of their natural component hierarchy, placing content anywhere in the document. The current stable version is 3.0.0, which targets Vue 3 and ships with full TypeScript types. While the project had a prolonged beta period, the 3.x line is now considered stable and actively maintained, receiving dependency updates and bug fixes as needed. Its primary differentiator is providing a robust, battle-tested solution for portal functionality directly within the Vue ecosystem, often used for modals, tooltips, and other overlay elements, without relying on imperative DOM manipulation. Vue 3 also offers its native `<Teleport>` component, which addresses many typical portal use cases, but PortalVue remains relevant for scenarios involving content movement between application components rather than simply moving to a specific DOM target.
Common errors
-
TypeError: app.use is not a function OR Cannot read properties of undefined (reading 'use')
cause Attempting to use `Vue.use()` with `createApp()` in a Vue 3 application, or `app` is not a valid Vue application instance.fixFor Vue 3, `Vue.use()` is replaced by `app.use()`. Ensure you have a `createApp()` instance, e.g., `const app = createApp(App); app.use(PortalVue);`. -
Unknown custom element: <portal> - did you register the component correctly?
cause The `<Portal>` or `<PortalTarget>` components were not globally registered via the plugin, or not locally registered where used.fixEnsure `app.use(PortalVue)` is called in your main application file before mounting your app, or import `{ Portal, PortalTarget } from 'portal-vue'` and register them locally in your components. -
Property 'hasContentFor' does not exist on type 'Wormhole'
cause The `Wormhole.hasContentFor()` method was removed in PortalVue 2.0.0, although it was later brought back in 2.1.0. This error indicates attempting to use it in a version where it's not available or a TypeScript definition issue.fixEnsure you are on PortalVue 2.1.0+ (or 3.x) if using `Wormhole.hasContentFor()`. For older 2.x versions, a workaround using `!!Wormhole.transports[name].length` might be necessary, though this is not public API. -
Error: Hydration completed but contains mismatches.
cause Mismatch between server-rendered and client-rendered DOM, often due to PortalVue's client-side only DOM manipulation during SSR.fixReview the PortalVue SSR documentation. During SSR, `<PortalTarget>` components must appear after their corresponding `<Portal>` components in the DOM. Consider dynamically rendering portal content client-side or using a custom target element.
Warnings
- breaking PortalVue v3.x is a complete rewrite for Vue 3 and is not compatible with Vue 2 applications. Projects migrating from PortalVue v2.x to v3.x must also upgrade their Vue version to 3.x.
- breaking The `<MountingPortal>` component, present in PortalVue v2.x, has been removed in v3.x. Its functionality for simple cases can often be replaced by Vue 3's native `<Teleport>` component. For moving content from multiple sources to a single mounted portal, a `createPortalTarget()` utility function is now available.
- breaking The `targetEl` prop on `<PortalTarget>` was removed in PortalVue 2.0.0 (and thus not present in 3.x), with its functionality moving to a separate component (MountingPortal, which was then removed in v3.x).
- gotcha During Server-Side Rendering (SSR), `Wormhole.open()` and `Wormhole.close()` methods are disabled and will not perform any actions. Content portal-ing only occurs on the client side during SSR.
- gotcha The syntax for defining transitions on the `<PortalTarget>` side has been redesigned in PortalVue 3.0, becoming more verbose and using `v-slot` syntax.
- gotcha Vue 3's native `<Teleport>` component addresses many common portal use cases (e.g., moving modals to `<body>`). While PortalVue offers more features for moving content between *app components*, evaluate if `<Teleport>` suffices for your specific need before integrating PortalVue.
Install
-
npm install portal-vue -
yarn add portal-vue -
pnpm add portal-vue
Imports
- PortalVue
import { PortalVue } from 'portal-vue'; const PortalVue = require('portal-vue');import PortalVue from 'portal-vue';
- Portal
import Portal from 'portal-vue'; const { Portal } = require('portal-vue');import { Portal } from 'portal-vue'; - PortalTarget
import PortalTarget from 'portal-vue'; const { PortalTarget } = require('portal-vue');import { PortalTarget } from 'portal-vue'; - Wormhole
const { Wormhole } = require('portal-vue');import { Wormhole } from 'portal-vue'; - PortalProps
import type { PortalProps } from 'portal-vue';
Quickstart
import { createApp } from 'vue';
import PortalVue from 'portal-vue';
const App = {
template: `
<div>
<h1>PortalVue Example</h1>
<button @click="count++">Increment Count: {{ count }}</button>
<Portal to="destination">
<p>This slot content, including reactive data, will be rendered wherever the <PortalTarget> with name 'destination' is located.</p>
<p>Current Portal Count: {{ count }}</p>
</Portal>
<div style="border: 2px dashed #007bff; padding: 20px; margin-top: 30px; background-color: #e6f7ff;">
<h2>Outside the Component Tree (Portal Target)</h2>
<PortalTarget name="destination">
<!-- Content from the Portal above will appear here -->
</PortalTarget>
</div>
</div>
`,
data() {
return {
count: 0
};
}
};
const app = createApp(App);
app.use(PortalVue); // Globally registers <Portal> and <PortalTarget> components
app.mount('#app');