Fix: expose() Should Be Called Only Once Per setup() in Vue.js

Error message:
expose() should be called only once per setup().
Composition API 2025-01-25

What Causes This Warning?

This warning occurs when you call expose() more than once in a component’s setup function. Vue only processes the first expose() call and ignores subsequent ones.

The Problem

export default {
  setup(props, { expose }) {
    // ❌ Multiple expose calls
    expose({ methodA })
    expose({ methodB }) // Warning! This is ignored
  }
}

The Fix

Single expose() Call

export default {
  setup(props, { expose }) {
    function methodA() { /* ... */ }
    function methodB() { /* ... */ }
    const publicValue = ref(0)

    // ✅ Single expose with all public members
    expose({
      methodA,
      methodB,
      publicValue
    })
  }
}

With defineExpose (Script Setup)

<script setup>
function methodA() { /* ... */ }
function methodB() { /* ... */ }
const publicValue = ref(0)

// ✅ Single defineExpose call
defineExpose({
  methodA,
  methodB,
  publicValue
})
</script>

Common Scenarios

Conditional Exposure

// ❌ Wrong - multiple exposes
export default {
  setup(props, { expose }) {
    if (props.advanced) {
      expose({ advancedMethod })
    }
    expose({ basicMethod }) // Ignored!
  }
}

// ✅ Correct - single expose
export default {
  props: ['advanced'],
  setup(props, { expose }) {
    const methods = {
      basicMethod
    }

    if (props.advanced) {
      methods.advancedMethod = advancedMethod
    }

    expose(methods)
  }
}

Composable Integration

// ❌ Each composable calling expose
function useFeatureA(expose) {
  expose({ featureA }) // Wrong!
}

function useFeatureB(expose) {
  expose({ featureB }) // Wrong!
}

// ✅ Collect and expose at component level
function useFeatureA() {
  return { featureA }
}

function useFeatureB() {
  return { featureB }
}

export default {
  setup(props, { expose }) {
    const a = useFeatureA()
    const b = useFeatureB()

    expose({
      ...a,
      ...b
    })
  }
}

Building Object Gradually

<script setup>
// ✅ Build object before exposing
const exposed = {}

exposed.method1 = () => { /* ... */ }
exposed.method2 = () => { /* ... */ }
exposed.value = ref(0)

defineExpose(exposed)
</script>

When to Use expose()

// Use expose to limit what parent can access via template ref
export default {
  setup(props, { expose }) {
    const internalState = ref('private')
    const publicState = ref('public')

    function internalMethod() { /* private */ }
    function publicMethod() { /* accessible via ref */ }

    // Only publicState and publicMethod accessible
    expose({
      publicState,
      publicMethod
    })

    return { internalState, publicState }
  }
}
<!-- Parent -->
<template>
  <ChildComponent ref="child" />
</template>

<script setup>
const child = ref(null)

onMounted(() => {
  // Only exposed members available
  child.value.publicMethod()  // ✅ Works
  child.value.publicState     // ✅ Works
  child.value.internalState   // ❌ Undefined
  child.value.internalMethod  // ❌ Undefined
})
</script>

Quick Checklist

  • Call expose() or defineExpose() only once
  • Combine all public members in a single object
  • Build the expose object before the call if needed
  • Composables should return values, not call expose