Skip to content

Context Inheritance Example

This example shows how to properly pass context to dynamically mounted components.

The Problem

Dynamically mounted components don't automatically have access to:

  • Vue Router
  • Pinia/Vuex store
  • Vue I18n
  • Provided values

Vue 3 Solution

Pass the app instance:

vue
<template>
  <button @click="showModal">Show Modal</button>
</template>

<script setup>
import { inject } from 'vue'
import { mount } from 'vue-mount-plugin'
import Modal from './Modal.vue'

// Get app from parent
const app = inject('app')

function showModal() {
  mount(Modal, {
    app: app,
    props: { title: 'Hello' }
  })
}
</script>

Setup in main.ts

typescript
// main.ts
import { createApp } from 'vue'
import { createRouter } from 'vue-router'
import { createPinia } from 'pinia'
import { createI18n } from 'vue-i18n'
import App from './App.vue'

const router = createRouter({ ... })
const pinia = createPinia()
const i18n = createI18n({ ... })

const app = createApp(App)

app.use(router)
app.use(pinia)
app.use(i18n)

// Provide for context inheritance
app.provide('app', app)

app.mount('#app')

Using Context in Mounted Component

vue
<!-- Modal.vue -->
<template>
  <div class="modal">
    <h2>{{ t('modal.title') }}</h2>
    <p>Route: {{ route.path }}</p>
    <p>User: {{ userStore.name }}</p>
  </div>
</template>

<script setup>
import { useRoute } from 'vue-router'
import { useUserStore } from '@/stores/user'
import { useI18n } from 'vue-i18n'

const route = useRoute()
const userStore = useUserStore()
const { t } = useI18n()
</script>

Vue 2 Solution

Pass parent for context inheritance:

vue
<template>
  <button @click="showModal">Show Modal</button>
</template>

<script>
import { mount } from 'vue-mount-plugin'
import Modal from './Modal.vue'

export default {
  methods: {
    showModal() {
      mount(Modal, {
        parent: this,
        props: { title: 'Hello' }
      })
    }
  }
}
</script>

Using Context in Mounted Component

vue
<!-- Modal.vue -->
<template>
  <div class="modal">
    <h2>{{ $t('modal.title') }}</h2>
    <p>Route: {{ $route.path }}</p>
    <p>User: {{ $store.state.user.name }}</p>
  </div>
</template>

Explicit Context

For more control, pass context explicitly:

typescript
mount(Modal, {
  parent: this,
  context: {
    router: this.$router,
    store: this.$store,
    i18n: this.$i18n
  }
})

Composable for Context

Create a reusable composable:

typescript
// useMountWithContext.ts
import { inject } from 'vue'
import { mount } from 'vue-mount-plugin'

export function useMountWithContext() {
  const app = inject('app')

  return <T extends Component>(
    component: T,
    options?: MountOptions
  ) => {
    return mount(component, {
      ...options,
      app
    })
  }
}

Usage

typescript
import { useMountWithContext } from './useMountWithContext'
import Modal from './Modal.vue'

const mountWithContext = useMountWithContext()

function showModal() {
  mountWithContext(Modal, {
    props: { title: 'Hello' }
  })
}

Context Demo Component

vue
<!-- ContextDemo.vue -->
<template>
  <div class="demo">
    <h2>{{ title }}</h2>
    <div class="status">
      <p>Router: {{ hasRouter ? '✅' : '❌' }}</p>
      <p>Store: {{ hasStore ? '✅' : '❌' }}</p>
      <p>I18n: {{ hasI18n ? '✅' : '❌' }}</p>
    </div>
    <button @click="emitResult">Close</button>
  </div>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
  title: String
})

const emit = defineEmits(['context-check', 'close'])

// Check what's available
const hasRouter = computed(() => {
  try {
    const route = useRoute()
    emit('context-check', 'Router: OK')
    return true
  } catch {
    emit('context-check', 'Router: Missing')
    return false
  }
})

const hasStore = computed(() => {
  try {
    const store = useStore()
    return !!store
  } catch {
    return false
  }
})

const hasI18n = computed(() => {
  try {
    const { t } = useI18n()
    return !!t
  } catch {
    return false
  }
})

function emitResult() {
  emit('close')
}
</script>

Released under the MIT License.