Gathering detailed insights and metrics for @linusborg/vue-simple-portal
Gathering detailed insights and metrics for @linusborg/vue-simple-portal
Gathering detailed insights and metrics for @linusborg/vue-simple-portal
Gathering detailed insights and metrics for @linusborg/vue-simple-portal
A simpler Portal implementation focussed on moving slot content to the end of the body element
npm install @linusborg/vue-simple-portal
Typescript
Module System
JavaScript (97.38%)
Vue (2.43%)
HTML (0.18%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
NOASSERTION License
224 Stars
99 Commits
28 Forks
5 Watchers
20 Branches
11 Contributors
Updated on Oct 23, 2024
Latest Version
0.1.5
Package Id
@linusborg/vue-simple-portal@0.1.5
Size
10.88 kB
Published on
Jan 06, 2021
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
vue-simple-portal
allows you to mount its slot content to the very end of the body element (or potentially any other DOM element on the page) as an immediate child.
Its main use case are components/elements that need to be positioned absolutely relative to the document/viewport, or fixed in some way or another, like:
Minimal example:
1<body> 2 <script src="https://unpkg.com/vue/dist/vue.js"></script> 3 <script src="https://unpkg.com/@linusborg/vue-simple-portal"></script> 4 <div id="app"> 5 <!-- your Vue app mounts to this element --> 6 </div> 7 8 <div id="portal-target"> 9 <!-- our `<portal>` should move stuff here --> 10 </div> 11 12 <script> 13 new Vue({ 14 el: '#app', 15 template: ` 16 <div> 17 <portal selector="#portal-target"> 18 <p>This will be mounted as a child element 19 of <div id="portal-target"> instead of 20 somewhere inside the child tree of <div id="app"> 21 </portal> 22 </div> 23 ` 24 }) 25 </script> 26</body>
portal-vue
I'm the author of portal-vue, a pretty popular portal library for Vue.js, so the obvious question is:
Why publish another Portal component?
Well, portal-vue
was my first successful library, and I wanted it to be awesome, so I packed it full of features to portal anything to anywhere, anytime you want.
That turned out pretty well, but also means the library is not exactly tiny anymore, and there also were a few issues that I found over time, so I wrote a smaller lib that addresses these issues while slimming down on features and (= bundle size) in order to concentrate on the main use case.
portal-vue
<body>
element so they could properly style and position their modals and similar components. For them, portal-vue comes with a lot of extra pounds that they don't need.$parent <-> $children
chain between the host component and the children that were moved. That also means a couple of things that rely on this chain internally don't work as expected, for example:
provide/inject
<route-view>
So I experimented a little and came up with this library here, which solves these two things for the majority of users:
$parent <-> $children
chain intact, so most of the existing caveats of portal-vue
are gone.vue-simple-portal
when you want to move stuff to the end of the document only.portal-vue
when you want to do more edge-case things, i.e. move stuff around to anywhere within your existing app, like the header component area, dynamically move the same content to different places by changing the destination prop etc.1npm install -D @linusborg/vue-simple-portal 2# or 3yarn add -D @linusborg/vue-simple-portal
This will make the <portal>
component available globally, but also make the portal not lazy-loadable.
1// main.js 2import Vue from 'vue' // requires Vue >= 2.6 3import VuePortal from '@linusborg/vue-simple-portal' 4 5Vue.use(VuePortal, { 6 name: 'portal', // optional, use to rename component 7})
1// in a component definition 2import { Portal } from '@linusborg/vue-simple-portal' 3export default { 4 name 'MyComponent', 5 components: { 6 Portal 7 } 8}
Just include it with a script tag after Vue itself
1<head> 2 <script src="https://unpkg.com/vue/dist/vue.js"></script> 3 <script src="https://unpkg.com/vue-simple-portal"></script> 4</head>
The plugin will automatically register the <portal>
component globally.
<portal>
works out of the box without setting any props. By default, it will:
<div>
with that id to the <body>
(once)<portal>
's slot content as a small, transparent component to that <div>
Which means the content is now an almost direct decendant of <body>
, and can safely and reliably be positioned absolutely etc.
So it's even easier than in the Usage Example from above:
1<portal> 2 <p>This will be mounted as a child element 3 of the auto-generated <div> instead of 4 somewhere inside the child tree of <div id="app"> 5</portal>
As shown in the initial Usage Example, you can use the selector
prop to append to an element of your choosing.
If you are tired of specifying the selector over and over again, you can set it as the default selector, overwriting the randomly generated one:
1Vue.use(VuePortal, { 2 selector: '#your-target', 3})
If you don't want to install the plugin globally, you can use the setSelector
helper:
1import { setSelector } from '@linusborg/vue-simple-portal' 2setSelector('#your-target')
This library aims to do one thing only, and do it well: move stuff to the end of the document for things like modals. But it still gives the developer a few props to influence how exactly that happens:
selector
type | required | default |
---|---|---|
String | no | 'vue-portal-target-<randomId>' |
A query selector that defines the target element to which to append the content of the portal.
If no selector is given, a random default selector (created at startup) is used.
If no element matches the selector, a new element is created and appended to <body>
Consequently, this means that using the <portal>
without a selector
prop will always append to a div at the end of the <body>
element, which is a sensible default for the goal of this plugin.
disabled
type | required | default |
---|---|---|
Boolean | no | false |
When true
, renders the <portal>
's slot content in place instead of appending it to the target element. See Caveats section for a small footgun here.
prepend
type | required | default |
---|---|---|
Boolean | no | false |
Usually, the slot content of a portal will be appended to the target element, which means, if that element has child nodes, our content will be inserted as the last node in that list.
Set the prepend
prop if you want to prepend the content instead.
tag
type | required | default |
---|---|---|
String | no | 'DIV' |
When the content of <portal>
is appended to the target element, it's actually wrapped in a small, transparent component (for technical reasons). Like all (non-functional) components in Vue, it requires a single root element.
The tag
prop can be used to define what that element should be.
Heads up: When used in combination with disabled
, it's used to define the root element of the <portal>
itself!
Some caveats still exist, such as:
disabled
propWhen you toggle the disabled
prop from true
to false
or vice versa, any components inside of the portal will be destroyed in their current location and re-created in their new location.
This means all their local state is lost.
If you need to keep state between these switches, keep it in a global state manager
When you use a <transition>
as the root element of the portal and then remove the portal (i.e. with v-if
) or set its disabled
prop to true
, no leave transition will happen.
While this is to expected, as the same thing would happen if you removed a div that contains a <transition>
, it often trips people up, which is why it's mentioned here.
If you need to remove or disable the portal after a transition has finished, you can make it work like this:
1<template> 2 <portal :disabled="disablePortal"> 3 <transition name="fade" appear @afterLeave="disablePortal = true"> 4 <div v-if="showTransitionContent"> 5 this will fade in/out 6 </div> 7 </transition> 8 </portal> 9</template> 10<script> 11 export default { 12 data: () => ({ 13 showTransitionContent: true, 14 disablePortal: false, 15 }), 16 methods: { 17 getOut() { 18 // calling this method will trigger the transition, 19 // which when finished, will disable the Portal 20 // through the `afterLeave` hook callback 21 this.showTransitionContent = false 22 23 } 24 } 25 } 26</script>
Of course this only works if you actually can listen to the events of the <transition>
, and could be problematic or even impossible with 3rd-Party components, depending on their implementations.
As a last resort you can always use a Timeout if you know the duration of the leave transition.
If the slot content of the <portal>
contains components, they will show up as children of the <portal>
in the devtools, even though their root elements were mounted/moved to the target element, outside of the current component tree.
The general advice is to only mount to elements outside of your Vue app, as that's the prime use case of this library. If you need to mount to locations inside of your app, consider using portal-vue instead.
That being said, you can move content to an element that is controlled by Vue, i.e. is part of the template of some other component.
However, be advised that this element could be removed or replaced by Vue when that component re-renders, while the component instances bound to that element (or its children) are still in memory. This could potentialy be a memory leak if you're not careful.
1yarn install
1yarn run serve
1yarn run build
1yarn run lint
1yarn run test
1yarn run test:e2e
1yarn run test:unit
This project is based on Vue CLI, so see Configuration Reference of Vue CLI for further details.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 6/14 approved changesets -- score normalized to 4
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
148 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More