useOverlay

A composable to programmatically control overlays.

Usage

Use the auto-imported useOverlay composable to programmatically control Modal and Slideover components.

<script setup lang="ts">
import { LazyModalExample } from '#components'

const overlay = useOverlay()

const modal = overlay.create(LazyModalExample)

async function openModal() {
  modal.open()
}
</script>
  • The useOverlay composable is created using createSharedComposable, ensuring that the same overlay state is shared across your entire application.
In order to return a value from the overlay, the overlay.open() can be awaited. In order for this to work, however, the overlay component must emit a close event. See example below for details.

API

useOverlay()

The useOverlay composable provides methods to manage overlays globally. Each created overlay returns an instance with its own methods.

create()

create(component: T, options: OverlayOptions): OverlayInstance

Create an overlay, and return a factory instance.

Parameters

component
T required
The overlay component to render.
options
OverlayOptions
Configuration options for the overlay.

on()

on(event: string, callback: (value: unknown) => void): void

Parameters

event
string required
The event to listen to.
callback
(value: unknown) => void required
The callback to invoke when the event is emitted.
Emits and its callback arguments are infer from component but it's not working for component which have more than 5 emits.

open()

open(id: symbol, props?: ComponentProps<T>): OpenedOverlay<T>

Open an overlay by its id.

Parameters

id
symbol required
The identifier of the overlay.
props
ComponentProps<T>
An optional object of props to pass to the rendered component.

close()

close(id: symbol, value?: any): void

Close an overlay by its id.

Parameters

id
symbol required
The identifier of the overlay.
value
any
A value to resolve the overlay promise with.

patch()

patch(id: symbol, props: ComponentProps<T>): void

Update an overlay by its id.

Parameters

id
symbol required
The identifier of the overlay.
props
ComponentProps<T> required
An object of props to update on the rendered component.

unmount()

unmount(id: symbol): void

Remove an overlay from the DOM by its id.

Parameters

id
symbol required
The identifier of the overlay.

isOpen()

isOpen(id: symbol): boolean

Check if an overlay is open using its id.

Parameters

id
symbol required
The identifier of the overlay.

overlays

overlays: Overlay[]

In-memory list of all overlays that were created.

Instance API

on()

on(event: string, callback: (value: unknown) => void): void

Listen to overlay's component emits.

Parameters

event
string required
The event to listen to.
callback
(value: unknown) => void required
The callback to invoke when the event is emitted.
<script setup lang="ts">
import { LazyModalExample } from '#components'

const overlay = useOverlay()

const modal = overlay.create(LazyModalExample)
modal.on('event-emitted', () => {
  console.log('event emitted')
})

function openModal() {
  modal.open({
    title: 'Welcome'
  })
}
</script>

open()

open(props?: ComponentProps<T>): Promise<OpenedOverlay<T>>

Open the overlay.

Parameters

props
ComponentProps<T>
An optional object of props to pass to the rendered component.
<script setup lang="ts">
import { LazyModalExample } from '#components'

const overlay = useOverlay()

const modal = overlay.create(LazyModalExample)

function openModal() {
  modal.open({
    title: 'Welcome'
  })
}
</script>

close()

close(value?: any): void

Close the overlay.

Parameters

value
any
A value to resolve the overlay promise with.

patch()

patch(props: ComponentProps<T>): void

Update the props of the overlay.

Parameters

props
ComponentProps<T> required
An object of props to update on the rendered component.
<script setup lang="ts">
import { LazyModalExample } from '#components'

const overlay = useOverlay()

const modal = overlay.create(LazyModalExample, {
  title: 'Welcome'
})

function openModal() {
  modal.open()
}

function updateModalTitle() {
  modal.patch({ title: 'Updated Title' })
}
</script>

Example

Here's a complete example of how to use the useOverlay composable:

<script setup lang="ts">
import { ModalA, ModalB, SlideoverA } from '#components'

const overlay = useOverlay()

// Create with default props
const modalA = overlay.create(ModalA, { title: 'Welcome' })
const modalB = overlay.create(ModalB)

const slideoverA = overlay.create(SlideoverA)

modalA.on('close', (value) => {
  console.log(value)
})

const openModalA = () => {
  // Open modalA, but override the title prop
  modalA.open({ title: 'Hello' })
}

const openModalB = async () => {
  // Open modalB
  const modalBInstance = modalB.open()

  /// Open the slideover when modalB closes
  /// - Using the callback argument of the `on` method
  modalB.on('close', (input) => {
    slideoverA.open({ input })
  })

  /// - Using the promise returned by `open`
  // Wait for opening result (resolved automatically when the overlay is closed)
  const input = await modalBInstance

  // Pass the result from modalB to the slideover, and open it
  slideoverA.open({ input })
}
</script>

<template>
  <button @click="openModalA">Open Modal</button>
</template>

In this example, we're using the useOverlay composable to control multiple modals and slideovers.

Caveats

Provide / Inject

When opening overlays programmatically (e.g. modals, slideovers, etc), the overlay component can only access injected values from the component containing UApp (typically app.vue or layout components). This is because overlays are mounted outside of the page context by the UApp component.

As such, using provide() in pages or parent components isn't supported directly. To pass provided values to overlays, the recommended approach is to use props instead:

<script setup lang="ts">
import { LazyModalExample } from '#components'

const providedValue = inject('valueProvidedInPage')

const modal = overlay.create(LazyModalExample, {
  props: {
    providedValue,
    otherData: someValue
  }
})
</script>

Limitation for components with more than 5 emits

Because of TypeScript limitation for infer overloaded functions in conditional types (cf: https://github.com/microsoft/TypeScript/issues/32164).

Because of this limitation, you could expect typing issues for components with more than 5 defined emits. This limitation is set 5 as we use a trick to make work for most cases.