Like native Vue‘s built-in <Teleport>
component, but different.
Was trying to refactor some project codebase from portal-vue
to built-in Vue‘s
<teleport>
component and couldn't make it work with TransitionGroup
. After
a discussion on a Vue-related issue,
I realized that it was a limitation due to its implementation.
I decided to keep portal-vue
in the project, as I needed the extra features, but
there were also some caveats
with its implementation and I start to wonder how hard would be to implement a
very minimalistic version of a teleport myself.
This package is the middle-ground between simplicity and power:
- Try using built-in Vue‘s
<teleport>
first. No point in bringing a 3rd party lib into your app when you only need simple teleport capabilities, like sending modals/dropdowns to end of<body>
. - Use
teleporte
if you need good support for in-app dynamic transportation of content, with conditional/deferred rendering of both origin/target; when customTransitionGroup
is required at target level or whenprovide/inject
is heavily used. - Use
portal-vue
for everything else.
This is not a feature parity conversion, these are the quirks in the other Vue "teleport" implementations that motivated me to build this package in the first place.
Quirks list | Vue's <teleport> |
portal-vue |
teleporte |
---|---|---|---|
Works when target is mounted after teleport origin | x⁵ | ✔️ | ✔️ |
Teleported content can use provide/inject origin context | ✔️ | x | ✔️ |
Teleport Target can use <TransitionGroup> |
x | ✔️¹ | ✔️¹ |
use of $parent | (TBD) | x | (TBD) |
vue-router view | (TBD) | x | x (will not support) |
use of $refs | ✔️ | ✔️² | ✔️³ |
SSR support | ✔️⁴ | ✔️⁴ | ✔️⁴ |
- Requires usage of target component
#default
slot bindings and loop over exposed vnodes directly intoTransitionGroup
default slot - after
nextTick
(see caveats in docs) - to assert the need for
nextTick
- Yes with caveats (see SSR Section)
- Will be supported without workarounds in 3.5.x minor
# npm | yarn
pnpm install teleporte
Warning
This package requires vue@^3.2.0
to be installed.
Then Import the components.
<sript setup>
import { TeleportOrigin, TeleportTarget } from 'teleporte'
</script>
<!-- Same API as portal-vue, in fact snippet is from their repo -->
<teleport-origin to="destination">
<p>This slot content will be rendered wherever the
<teleport-target> with name 'destination'
is located.
</p>
</teleporte>
<teleport-target name="destination">
<!--
This component can be located anywhere in your App
(i.e. right before the </body> tag, good for overlays).
The slot content of the above teleporte component will be rendered here.
-->
</teleport-target>
import { TeleportPlugin } from 'teleporte'
import { createApp } from 'vue'
const app = createApp()
app.use(TeleportPlugin)
// exposes Teleporte and TeleportTarget as global components
<sript setup>
import { Teleporte, TeleporteTarget } from 'teleporte'
</script>
<teleport-origin to="destination">
<p>This slot content will be rendered wherever the
<teleport-target> with name 'destination'
is located.
</p>
</teleport-origin>
<teleport-target name="destination" #default="teleported">
<transition-group>
<component
v-for="teleport in teleported"
:key="teleport.key"
:is="teleport.component"
/>
</transition-group>
</teleport-target>
TL;DR we use a singleton pattern for storing teleports
state so the module state is preserved on each request which would lead to content duplication. We prevent that within library code.
Since the content will not render on server, you'll get a warning from Vue. The same caveat is present in the other "teleport" implementations so the workarounds are the same:
- defer
<teleport-origin>
mount with aref
atonMounted
hook +v-if
- Your SSR framework (like
nuxt
) will probably provide a<client-only>
component, so wrap inside it - There are external
<client-only>
component implementations out there as well or you can roll your own.
See:
- https://vuejs.org/guide/scaling-up/ssr#cross-request-state-pollution
- https://vuejs.org/guide/scaling-up/ssr#teleports
- https://portal-vue.linusb.org/guide/ssr.html
See the guide at CONTRIBUTING.md
- To be deprecated if Vue‘s
<teleport>
adds fixes to the current quirks :)- vuejs/core#2015
- vuejs/core#4737
- vuejs/core#5836 (comment) (the issue was closed, but it still doesn't work for the
<TransitionGroup>
use case) - vuejs/core#5864 (closed by vuejs/core#11387)
- vuejs/core#2015 (comment)
- While that does not happen, this project aims to be a just minimalistic enhanced
version of
<teleport>
:- bundle size to be kept below around
≈1.2KB gzip
- No unnecessary features: it should do one thing and do it well, move content between target and origin without quirks.
- bundle size to be kept below around
portal-vue
, the main inspiration for this package implementation- Vue‘s
<teleport>
quirks for the motivation to do it - To all maintainers from the packages in dependencies.
- Teleporte is the Portuguese word for teleport. I found it curious how similar it was and in my mind the "e" suffix could also mean "enhanced": Teleport enhanced.
- The original version started as a Vue playground, which I've then translated into this more generic package.