PortalTarget

This component is an outlet for any content that was sent by a <Portal> component. It renders received content and doesn't do much else.

Example usage

<portal-target name="destination" />
1

This is an abstract component which means it will not be visible in vue-devtools

Props API

multiple 1.2.0+

When multiple is true, the portal will be able to receive and render content from multiple <Portal> component at the same time.

You should use the order prop on the <Portal> to define the order in which the contents should be rendered:

Source










<portal to="destination" :order="2">
  <p>some content</p>
</portal>
<portal to="destination" :order="1">
  <p>some other content</p>
</portal>

<portal-target name="destination" multiple />
1
2
3
4
5
6
7
8

Result

<div class="vue-portal-target">
  <p>some other content</p>
  <p>some content</p>
</div>
1
2
3
4

Usage with `slim`

multiple may not behave as expected when its <PortalTarget> is also in slim mode because slim attempts to assign the content's root node as the <PortalTarget>'s root node, thereby only rendering the first of all incoming nodes.

name

Type Required Default
String yes none

Defines the name of this portal-target. <Portal> components can send content to this instance by this name.

slim

Type Required Default
Boolean no false

When set to true, the component will check if the sent content has only one root node. If that is the case, the component will not render a root node of its own but instead just return that single node.

Source





<portal to="destination"> <p>Only one content element</p> </portal>

<portal-target name="destination" slim />
1
2
3

Result

<p>Only one content element</p>
1

BREAKING CHANGE IN 2.0.0

When there's no content and slim is set, the target doesn't render an empty <div> anymore, it renders nothing (a comment node as a placeholder is rendered, to be precise).

slotProps 1.3.0+

Type Required Default
Object no {}

TIP

This prop is only useful when the PortalTarget received content from a scoped Slot of a <Portal>.

The slotProps object is used as props to render the scoped slot from a <Portal>.

Source


 




<portal to="destination">
  <p slot-scope="props">This scoped slot content is so {{ props.state }}</p>
</portal>

<portal-target name="destination" slot-props="{state: 'cool!'}" />
1
2
3
4
5

Result

<div class="vue-portal-target">
  <p>This scoped slot content is so cool!</p>
</div>
1
2
3

It has a counterpart of the same name on the <Portal> component to pass props to the slot content when the <Portal> is disabled.

tag

Type Required Default
String no 'DIV'

Defines the type of tag that should be rendered as a root component.

Source



<portal-target name="destination" tag="span" />
1

Result

 



<span class="vue-portal-target">
  <!-- any content from <Portal> component may be rendered here -->
</span>
1
2
3

transition changed in 2.0.0+

Type Required Default
Boolean|String|Object no none

This property is used to configure a transition for the portal content. By default, it will render a <transition-group>, which will respect the <PortalTarget>'s tag property and will render instead of the plain wrapper element that is usually rendered.

It accepts:

  • a String value: will render a globally registered component of this name.
  • a Component: will render <transition-group> with the object's content passed as props.

Example with string:




 


<portal-target
  name="dest" 
  slim
  transition="fade">
</portal-target>
1
2
3
4
5

Example with Component:

<portal-target 
  name="dest" slim 
  :transition="fadeTransition">
</portal-target>
1
2
3
4
computed: {
  fadeTransition() {
    return {
      functional: true,
      render(h, context) {
        return h('transition', { props: { name: 'fade', mode: 'out-in' } }, context.children)
      }
    }
  },
  globalTransitionComponent() {
    // instead of creating the Transition component locally like above,
    // you probably would like to re-use a component, that you registered
    // either globally or locally.
    return Vue.component('yourGloballyregisteredComponent')
    // or
    return this.$options.components('yourLocallyregisteredComponent')
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Resuable Transition components

The Vue documentation has a section about creating resusable transititions with components

Slim Mode

When slim is also specified, it will render a <transition> instead of a <transition-group>.

transitionEvents removed in 2.0.0

Type Required Default
Object no none

This property requires that the `transition` prop is defined as well.

Accepts an object whose keys match the transition component's events. Each key's value should be a callback function for the transition.




 


<portal-target
  name="dest"
  transition="fade"
  :transition-events="{ enter: handleEnter, leave: handleLeave }"
></portal-target>
1
2
3
4
5

Slots API

Default slot 1.1.0+

Any existing slot content is rendered in case that no content from any source Portal is available.

Example:

Source


 


<portal-target name="destination" tag="span">
  <p>This is rendered when no other content is available.</p>
</portal-target>
1
2
3

Result

<span class="vue-portal-target">
  <p>This is rendered when no other content is available.</p>
</span>
1
2
3

Default scoped slot 2.0.0+

If a scoped slot is provided, its content is rendered in case that no content from any source Portal is available. The scoped slot receives the slotProps prop as its argument.

Example:

Source

 
 
 



<portal-target name="destination" :slotScope="{ message: 'Hi!' }">
  <p slot-scope="props">
    {{props.message}} This is rendered when no other content is available.
  </p>
</portal-target>
1
2
3
4
5

Result

<div class="vue-portal-target">
  <p>This is rendered when no other content is available.</p>
</div>
1
2
3

Events API 1.1.0+

change

Emitted everytime the component re-renders because the content from the <Portal> changed.

It receives two arguments, each is a Boolean, indicating the absense or presence of content for the target.




 










<template>
  <portal-target name="destination" @change="handleUpdate" />
</template>

<script>
  export default {
    methods: {
      handleUpdate(newContent, oldContent) {
        // do something with the info.
      },
    },
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

BREAKING CHANGE in 2.0.0

The event now is simple Boolean values, indicating wether the target is/was empty or not.

Previously, we emitted the old and new contents, but that code was too cumbersome for little value.