teleporte
v1.0.0-rc.3
Published
Like Vue‘s Teleport component, but different.
Downloads
367
Maintainers
Readme
teleporte
Like native Vue‘s built-in <Teleport>
component, but different.
Why another vue teleport package
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.
When should you use this package
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.
QuirksList
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 | ✔️⁴ | ✔️⁴ | ✔️⁴ |
Footnotes
- 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
Installation & basic usage.
# 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>
Install as Vue plugin
import { TeleportPlugin } from 'teleporte'
import { createApp } from 'vue'
const app = createApp()
app.use(TeleportPlugin)
// exposes Teleporte and TeleportTarget as global components
Usage with TransitionGroup
<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>
SSR
cross-request-state-pollution
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.
Hydration mismatches
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
Contribute
See the guide at CONTRIBUTING.md
Goals
- To be deprecated if Vue‘s
<teleport>
adds fixes to the current quirks :)- [x] https://github.com/vuejs/core/issues/2015
- [ ] https://github.com/vuejs/core/issues/4737
- [ ] https://github.com/vuejs/core/issues/5836#issuecomment-2230343828
- [ ] https://github.com/vuejs/core/issues/5864
- 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
Credits
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.
Footnotes
- 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.