React

Teleport in Vue 3: modali fuori dal DOM del genitore

Teleport sposta la modale sotto body, dove nessun overflow o transform del genitore la può tagliare. Il componente resta lo stesso, cambia il nodo.

In sintesi

  • Teleport sposta il DOM, non l’istanza: provide, inject ed emit continuano a funzionare.
  • Il v-if va dentro il Teleport, altrimenti il nodo viene creato e distrutto a ogni apertura.
  • Teleport non gestisce focus trap, scroll lock o chiusura con ESC: quelli restano da scrivere.

Una modale in Vue 3 esce dalla gerarchia visiva del genitore con <Teleport>: il nodo finisce sotto body, dove nessun overflow: hidden o transform lo può tagliare.

Il problema che risolve

Un pannello con position: fixed si posiziona rispetto all’antenato con transform, filter o perspective più vicino, non rispetto alla finestra. Dentro una card animata la modale appare ritagliata e l’overlay copre metà schermo.


import { ref } from "vue";

const open = ref(false);



  Open

  
    
      
        Title
        Close
      
    
  

L’attributo to accetta un selettore CSS o un elemento. Il v-if sta dentro il Teleport: così il contenuto non esiste nel DOM quando la modale è chiusa.

Un contenitore dedicato, oppure no


  
    ...
  


 -->

Un contenitore vuoto in index.html evita di appoggiarsi a body: se un altro script monta un elemento sul body, il tuo nodo resta tuo. Con :disabled il Teleport si spegne e il contenuto torna al suo posto, utile nei test.

Teleport, dialog nativo, libreria

CasoVincePerché
Modale con form e stato internoTeleport più componenteIl contenuto resta un componente Vue
Avviso breve senza focusTeleport su bodyBasta un nodo, niente gestione del focus
Focus trap, scroll lock, ESCLibreria headlessAccessibilità già scritta e testata
Progetto senza dipendenzeTeleportZero installazioni, il resto lo scrivi tu

Errori comuni

  • Mettere v-if sul Teleport invece che sul contenuto: il nodo target nasce e muore a ogni apertura.
  • Puntare a un id che non esiste: Vue avvisa in console e non renderizza nulla.
  • Dimenticare il focus: la modale si apre con il cursore rimasto sul pulsante dietro.
  • Lasciare il Teleport attivo in un test: il contenuto finisce fuori dal wrapper montato e le query falliscono.

Il limite da mettere in conto

Teleport sposta il DOM, non il contesto: provide, inject, emit e listener continuano a funzionare perché l’istanza non cambia. Non risolve però focus trap e blocco dello scroll, che restano da scrivere o da prendere da una libreria headless.

Dove continuare

Nel catalogo: shadcn-vue, PrimeVue e vue-bits; i blocchi di dialogo sono in /blocks. Le guide: Composition API e Pinia. I confronti: portal in React e Vue vs React.

Domande frequenti

Posso usare Teleport per una modale accessibile?

Sì, ma Teleport gestisce solo la posizione nel DOM. Focus trap, blocco dello scroll e chiusura con ESC vanno scritti o presi da una libreria headless.

Si perde il contesto del componente?

No. L’istanza resta la stessa: provide, inject, emit e stato reattivo funzionano come prima dello spostamento.

Perché la modale non compare?

Il selettore in to non trova nulla. Vue avvisa in console e salta il rendering: controlla che il nodo esista prima del mount.

Teleport funziona con il rendering lato server?

Sì, con un’avvertenza: sul server il contenuto viene reso dove è dichiarato. Usa l’attributo disabled durante l’idratazione per evitare disallineamenti.

Serve un contenitore dedicato?

Non è obbligatorio. body funziona, ma un nodo tuo evita conflitti con altri script che montano sullo stesso body.