{"id":11573,"library":"portal-vue","title":"PortalVue for Vue 3","description":"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.","status":"active","version":"3.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/LinusBorg/portal-vue","tags":["javascript","typescript"],"install":[{"cmd":"npm install portal-vue","lang":"bash","label":"npm"},{"cmd":"yarn add portal-vue","lang":"bash","label":"yarn"},{"cmd":"pnpm add portal-vue","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency for Vue 3 application runtime, requiring version `^3.0.4` or higher.","package":"vue","optional":false}],"imports":[{"note":"The default export is the plugin for global registration of `<Portal>` and `<PortalTarget>` components. Use `app.use(PortalVue)` in your Vue 3 application's entry file to make the components globally available.","wrong":"import { PortalVue } from 'portal-vue';\nconst PortalVue = require('portal-vue');","symbol":"PortalVue","correct":"import PortalVue from 'portal-vue';"},{"note":"The `<Portal>` component can be explicitly imported for local component registration or type inference. It is automatically registered globally when `app.use(PortalVue)` is called.","wrong":"import Portal from 'portal-vue';\nconst { Portal } = require('portal-vue');","symbol":"Portal","correct":"import { Portal } from 'portal-vue';"},{"note":"The `<PortalTarget>` component can be explicitly imported for local component registration or type inference. It is automatically registered globally when `app.use(PortalVue)` is called.","wrong":"import PortalTarget from 'portal-vue';\nconst { PortalTarget } = require('portal-vue');","symbol":"PortalTarget","correct":"import { PortalTarget } from 'portal-vue';"},{"note":"The `Wormhole` object provides programmatic control over portals, allowing you to `open()` or `close()` content to targets. It is rarely needed for typical usage but is part of the public API.","wrong":"const { Wormhole } = require('portal-vue');","symbol":"Wormhole","correct":"import { Wormhole } from 'portal-vue';"},{"note":"TypeScript types are available since v3.0.0, enabling strong typing for component props and other interfaces.","symbol":"PortalProps","correct":"import type { PortalProps } from 'portal-vue';"}],"quickstart":{"code":"import { createApp } from 'vue';\nimport PortalVue from 'portal-vue';\n\nconst App = {\n  template: `\n    <div>\n      <h1>PortalVue Example</h1>\n      <button @click=\"count++\">Increment Count: {{ count }}</button>\n      <Portal to=\"destination\">\n        <p>This slot content, including reactive data, will be rendered wherever the <PortalTarget> with name 'destination' is located.</p>\n        <p>Current Portal Count: {{ count }}</p>\n      </Portal>\n\n      <div style=\"border: 2px dashed #007bff; padding: 20px; margin-top: 30px; background-color: #e6f7ff;\">\n        <h2>Outside the Component Tree (Portal Target)</h2>\n        <PortalTarget name=\"destination\">\n          <!-- Content from the Portal above will appear here -->\n        </PortalTarget>\n      </div>\n    </div>\n  `,\n  data() {\n    return {\n      count: 0\n    };\n  }\n};\n\nconst app = createApp(App);\napp.use(PortalVue); // Globally registers <Portal> and <PortalTarget> components\napp.mount('#app');","lang":"typescript","description":"Demonstrates a basic Vue 3 application using PortalVue to 'teleport' reactive content from one part of the component tree to a `PortalTarget` located elsewhere in the DOM."},"warnings":[{"fix":"Upgrade your project to Vue 3.x and update PortalVue to 3.x. Consult the official migration guide for detailed steps.","message":"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.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"For single-source portals, consider using Vue 3's `<Teleport>`. For multiple sources, use `createPortalTarget()` as described in the PortalVue v3 documentation.","message":"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.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Refer to the migration guide for PortalVue 2.0.0 from older versions for `targetEl` alternatives, then apply the v3.x migration path if applicable.","message":"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).","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Ensure your SSR strategy accounts for content that is 'portal-ed' by PortalVue appearing only client-side. See the dedicated SSR guide in the documentation.","message":"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.","severity":"gotcha","affected_versions":">=2.1.2"},{"fix":"Update transition implementations on `<PortalTarget>` to use the new `v-slot` based syntax, passing a transition to a slot named `wrapper` and using Vue's `<component :is=\"nodes[0]\" />` to render the content.","message":"The syntax for defining transitions on the `<PortalTarget>` side has been redesigned in PortalVue 3.0, becoming more verbose and using `v-slot` syntax.","severity":"gotcha","affected_versions":">=3.0.0"},{"fix":"Consult Vue 3's official documentation on `<Teleport>` to determine if it meets your requirements for simpler portal scenarios.","message":"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.","severity":"gotcha","affected_versions":">=3.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"For Vue 3, `Vue.use()` is replaced by `app.use()`. Ensure you have a `createApp()` instance, e.g., `const app = createApp(App); app.use(PortalVue);`.","cause":"Attempting to use `Vue.use()` with `createApp()` in a Vue 3 application, or `app` is not a valid Vue application instance.","error":"TypeError: app.use is not a function OR Cannot read properties of undefined (reading 'use')"},{"fix":"Ensure `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.","cause":"The `<Portal>` or `<PortalTarget>` components were not globally registered via the plugin, or not locally registered where used.","error":"Unknown custom element: <portal> - did you register the component correctly?"},{"fix":"Ensure 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.","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.","error":"Property 'hasContentFor' does not exist on type 'Wormhole'"},{"fix":"Review 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.","cause":"Mismatch between server-rendered and client-rendered DOM, often due to PortalVue's client-side only DOM manipulation during SSR.","error":"Error: Hydration completed but contains mismatches."}],"ecosystem":"npm"}