Search code examples
javascriptvue.jsvuejs3

Conditionally wrap a vue 3 element


In Vue 3, how can an element be conditionally wrapped rather than creating two seperate blocks with v-if directive?

I have a simplified example here, but the question deals with conditional wrapping. There are a few issues here, in the html block there is a lot of coupling with state in the script setup block, so abstracting that to a child component is more trouble than it's worth. There would be mixed state and it would be a mess having to deal with emitting data from child to parent. Second, because of the size of the html block, creating manual render functions would also be a mess.

The below code does work, but there is a big block that is duplicated just because I need an extra wrapper. Note that this question is not about how css could be used to fix this.

<script setup>
const isMobile = ref(true)
</script>

<template>
  <div v-if="isMobile" class="mobile-wrapper">
    <div class="product-meta">
      <!-- long html block tightly coupled with script setup block -->
    </div>
  </div>
  <div v-else class="product-meta">
    <!-- same html block -->
  </div>
</template>

Solution

  • See on Vue SFC Playground

    import { h } from 'vue';
    export default function Wrap({wrapper, ...props}, {slots}){
      return wrapper ? h(wrapper, props, slots.default()) : slots.default();
    }
    

    Usage:

      <wrap :wrapper="isWrapped && 'div'" :="{class: 'wrapper'}">
        <p>
          I'm wrapped
        </p>
      </wrap>
    

    More complex options: https://dev.to/alexander-nenashev/series/27323