https://github.com/NoelDeMartin/vue-modals
Vue Modals done right
https://github.com/NoelDeMartin/vue-modals
vue
Last synced: about 1 month ago
JSON representation
Vue Modals done right
- Host: GitHub
- URL: https://github.com/NoelDeMartin/vue-modals
- Owner: NoelDeMartin
- License: mit
- Created: 2025-07-25T16:12:13.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2025-08-04T18:18:55.000Z (2 months ago)
- Last Synced: 2025-08-28T18:55:34.289Z (about 1 month ago)
- Topics: vue
- Language: TypeScript
- Homepage:
- Size: 261 KB
- Stars: 19
- Watchers: 0
- Forks: 3
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Vue Modals 
The missing library to do Vue Modals right:
- ๐ช Open modals from anywhere (even outside of Vue components!).
- ๐ค Return promises from modals for seamless async workflows.
- ๐งผ Clean and simple API.
- ๐ง Full TypeScript support.
- ๐ฆ Tiny library with zero dependencies.To learn more, read this blog post: [The Problems With Modals, and How to Solve Them](https://noeldemartin.com/blog/the-problems-with-modals-and-how-to-solve-them).
## Installation
First, install it using your favorite package manager:
```sh
pnpm add @noeldemartin/vue-modals
```Then, place a `` somewhere within your app (wherever you want to render modals).
Finally, open modals like this:
```js
import { showModal } from '@noeldemartin/vue-modals';
import MyModal from './MyModal.vue';const { answer } = await showModal(MyModal, { question: 'How many golf balls fit into a Boeing 747?' });
```## Usage
The second argument in the `showModal` function will be passed as component properties, and modals can be closed by emitting a `close` event (the payload of which will be returned in a promise). You can take advantage of Vue's TypeScript features to type both:
```vue
Close
defineEmits<{ close: [{ answer: string }] }>();
defineProps<{ question: string }>();```
```ts
const { answer } = await showModal(MyModal, { question: 'How many golf balls fit into a Boeing 747?' });
// ๐ answer will be typed as `string | undefined` (in case the modal is dismissed)
// ๐ showModal's second argument will be typed as `{ question: string }`
```### Modal responses
Given that modals can also be dismissed, the payload from the `close` event won't be directly returned in the promise. Instead, it will be merged into an object with a `dismissed` boolean:
```ts
defineEmits<{ close: [{ answer: string }] }>();
// ๐ showModal will return `Promise<{ dismissed: false; answer: string } | { dismissed: true; answer?: undefined }>`defineEmits<{ close: [string] }>();
// ๐ showModal will return `Promise<{ dismissed: false; response: string } | { dismissed: true; response?: undefined }>`defineEmits<{ somethingElse: [] }>();
// ๐ showModal will return `Promise<{ dismissed: boolean }>`
```### Customizing modals
If you want to have an overlay that sits behind your modals, you can use the `overlay` slot in the ``. You can also style the modals container passing class or style attributes, and use `` to implement animations:
```html
```
By default, modals will be rendered one after another in the DOM. However, some libraries may require to nest them (such as [Reka UI](https://reka-ui.com/docs/components/dialog#nested-dialog)). In that case, you can use the `nested` attribute to indicate that the rendering of child modals will happen in each modal component, rather than the root:
```vue
```
Modals don't need to use any special components, so they can be simple divs:
```html
My modal content
```However, if you also want to configure some animations, you can use the `` component and pass the same attributes used in the `` component:
```html
My modal content
```
Finally, if you want to create your own modal wrapper, you can use `useModal()`. This will expose a couple of utilities you can use to work with the modal. By default, modals are removed from the DOM when closed, but you can pass the `{ removeOnClose: false }` option to disable it. If you do, make sure to call `remove()` to remove it yourself. Combined with `visible`, and using the native ``, you can achieve the same result as the `` component to customize it on your own:
```vue
import { useModal } from '@noeldemartin/vue-modals/composition';
const { visible, close, remove } = useModal({ removeOnClose: false });
```
You can also use `child` if you want to render nested modals using the ``:
```vue
import { useModal } from '@noeldemartin/vue-modals/composition';
const { child, visible, close, remove } = useModal({ removeOnClose: false });
```
However, keep in mind that if you're going to work with modals this way, you'll need to implement all the accessibility functionality on your own (focus trapping, keyboard events, etc.).
Instead, you'll probably want to integrate with an existing component library.
### Third-party integrations
In order to use this with component libraries, you'll need to follow similar techniques from the ones described in the previous section.
There are some built-in integrations, but feel free to look at the [src/integrations/](./src/integrations/) folder to learn more.
#### PrimeVue
You can use the `` component as usual, but import `` from `@noeldemartin/vue-modals/primevue` instead. Attributes will be passed down to PrimeVue's native [``](https://primevue.org/dialog/#api.dialog.props) component, such as `header`, `footer`, `maximizable`, etc.
```vue
My modal content
import Button from 'primevue/button';
import { Modal } from '@noeldemartin/vue-modals/primevue';```
#### Shadcn
Following Shadcn's philosophy, this library doesn't export any code to integrate with the library. Instead, you'll want to copy & paste these into your project:
`src/components/ui/modal/Modal.vue`
```vue
import { Dialog, DialogContent } from '@/components/ui/dialog';
import { useModal, ModalComponent } from '@noeldemartin/vue-modals';const { child, ...modal } = useModal({ removeOnClose: false });
function close() {
modal.close();setTimeout(() => modal.remove(), 1000);
}```
`src/components/ui/modal/ModalsPortal.vue`
```vue
import { ModalsPortal } from '@noeldemartin/vue-modals';
```
`src/components/ui/modal/index.ts`
```ts
export { default as Modal } from './Modal.vue';
export { default as ModalsPortal } from './ModalsPortal.vue';
```Once that is set up, you should be able to create modals like this:
```vue
My Awesome Modal
Modal content goes here
import { Modal } from '@/components/ui/modal';
import { DialogHeader, DialogTitle } from '@/components/ui/dialog';```