Fix z-index war with VueJS Teleport
30 May 2023

If you have worked on a relative large frontend codebase before, you will realize how messy it gets when dealing with z-indexes. A quick google on “z-index best practices” results in a number of articles providing solutions: some with a certain degree of JavaScript, some uses SCSS with the help of SCSS variables and some just having a list of standardized z-index values on which the development team agrees upon. I would like to provide another solution that is much simpler and straightforward.

VueJS Teleport component

The Teleport component is a great solution to not think about what z-index value you should use when creating modals, confirmation dialogs, slide out drawers or any elements that you want to overlap the main interface.

Defining the teleport’s outlet

In the main HTML’s <body> element we can specify where our modals and dialogs will render at. With the layering properly defined, we wouldn’t need to recall which layer is above or below another.

<!doctype html>
<html lang="en">
  <head>
    ...
  </head>
  <body>
    ...
    <div id="app-top-menu"></div>
    <div id="app-drawers"></div>
    <div id="app-dialogs"></div>
    <div id="app-poppers"></div>
    <div id="app-announcements"></div>
  </body>
</html>

Using the <Teleport> component

The <Teleport> component has a to prop to specify which layer we want our element to be rendered at, it doesn’t get as simple as that! So for example, if we have a top fixed navigation menu, we do not need to specify any z-index even though it is declared near the top in document order. We can just wrap the <nav> with the <Teleport> component and then specify the id of the div that it renders on for the to prop.

<template>
  <Teleport to="#app-top-menu">
    <nav>...</nav>
  </Teleport>
</template>

Conclusion

I believe using the <Teleport> component leads to cleaner code with less ambiguity on your journey with z-indexes. I hope native HTML supports this pattern in the future.