{"id":19848109,"url":"https://github.com/shunnnet/vue-modal-route","last_synced_at":"2025-08-13T22:05:24.766Z","repository":{"id":255384830,"uuid":"849440450","full_name":"shunnNet/vue-modal-route","owner":"shunnNet","description":"A Vue 3 package that integrates vue-router with modal state management, enabling modal dialogs to be controlled via routes.","archived":false,"fork":false,"pushed_at":"2025-07-15T15:41:13.000Z","size":5040,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-08T23:55:32.384Z","etag":null,"topics":["dialog","modal","vue","vue-router","vue3"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@vmrh/core","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shunnNet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-08-29T15:44:40.000Z","updated_at":"2025-07-15T15:41:17.000Z","dependencies_parsed_at":"2024-11-12T13:16:20.365Z","dependency_job_id":"07597997-f1f0-47ea-9f77-4143580ce23f","html_url":"https://github.com/shunnNet/vue-modal-route","commit_stats":null,"previous_names":["shunnnet/vrm"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/shunnNet/vue-modal-route","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shunnNet%2Fvue-modal-route","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shunnNet%2Fvue-modal-route/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shunnNet%2Fvue-modal-route/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shunnNet%2Fvue-modal-route/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shunnNet","download_url":"https://codeload.github.com/shunnNet/vue-modal-route/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shunnNet%2Fvue-modal-route/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269671951,"owners_count":24457110,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dialog","modal","vue","vue-router","vue3"],"created_at":"2024-11-12T13:16:10.579Z","updated_at":"2025-08-13T22:05:24.290Z","avatar_url":"https://github.com/shunnNet.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vue-modal-route\n[![npm version](https://img.shields.io/npm/v/@crazydos/@vmrh/core.svg)](https://www.npmjs.com/package/@crazydos/@vmrh/core)\n[![npm downloads](https://img.shields.io/npm/dm/@crazydos/@vmrh/core.svg)](https://www.npmjs.com/package/@crazydos/@vmrh/core)\n[![License](https://img.shields.io/github/license/shunnNet/vue-modal-route.svg)](https://github.com/shunnNet/vue-modal-route/blob/main/LICENSE)\n\n`vue-modal-route` is a Vue 3 package that integrates modal state management with vue-router. It allows you to control modals via routes and pass complex data effortlessly — making modal handling more declarative, shareable, and router-friendly.\n\nUnlike Next.js-style modals, this package takes a different approach. If you're looking for route-driven modals similar to those in Next.js, consider using [nuxt-page-plus](https://nuxt-pages-plus.pages.dev/routing/modal-routes).\n\n## Features\nThis package is designed for more flexible modal scenarios and comes with several key features:\n\n- ✅ Use full vue-router capabilities inside your modal components — including router-view, navigation guards, and nested routes.\n- 🔗 Open modals via URL navigation, enabling deep linking and browser history support.\n- 📦 Pass complex data objects to modals, beyond the limitations of URL-encoded types.\n- 🧩 Supports a wide range of use cases — from simple alerts, login dialogs, to modals embedded in single-page views.\n- 👍 Not limited to a specific ModalUI library, you can use any ModalUI.\n\n---\n## Online example \u0026 playground\n[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/~/github.com/shunnNet/vmrh-vite-playground)\n\n## Why and How ?\nIf you need the motivation and implementation details for vue-modal-route, you can refer to this article.\n\nhttps://dev.to/shunnnet/implementing-vue-modal-route-58ff\n\n## Usage\n\n### Quick Start\nInstall it. \n\nAnd your project must already include vue-router:\n\n```sh\nnpm install @vmrh/core vue-router\n```\n\nTo get started, use `createModalRoute` to configure both `vue-router` and `@vmrh/core`.\n\nSet up any page as usual, and define modal routes under the children property of that route. For example:\n\n```ts\n// src/router.ts\nimport { createModalRoute } from '@vmrh/core'\n\nexport const router = createModalRoute({\n  routes: [\n    {\n      name: \"Index\",\n      path: '/',\n      component: () =\u003e import(\"./pages/Index.vue\"),\n      children: [\n        {\n          // \u003c-- Modal route\n          name: \"MyIndexModal\", // \u003c-- Modal route's name\n          path: 'index-modal',\n          component: () =\u003e import(\"./pages/IndexModal.vue\"),\n          meta: {\n            modal: true, // \u003c-- This makes it modal route.\n            direct: true, // \u003c-- This enable diretly access from url.\n          }\n        }\n      ]\n    }\n  ]\n})\n```\n\n\u003e [!NOTE]\n\u003e When using `createModalRoute`, **all routes must have a name**, and **the name must be of type string**.\n\nThen, register the router as a plugin in your app just like you would with regular `vue-router`:\n\n```ts\nimport App from './App.vue'\nimport { createApp } from 'vue'\n\nconst app = createApp(App)\napp\n  .use(router)\n  .mount('#app')\n```\n\nAnd add `\u003cRouterView\u003e` to your App.vue.\n\n```vue\n\u003ctemplate\u003e\n  \u003cRouterView /\u003e\n\u003c/template\u003e\n```\n\nNext, set up your modal route component (in the previous example, this would be `./pages/IndexModal.vue`).\n\nYou can use any modal component inside your modal route.\nBy calling `useCurrentModal`, you can access the current modal’s visible state via `modelValue`, and pass it into your own modal like this:\n\n```vue\n\u003c!-- ./pages/IndexModal.vue --\u003e\n\u003cscript setup lang=\"ts\"\u003e\nimport Modal from './path-to-my-modal'\nimport { useCurrentModal } from '@vmrh/core'\n\nconst { modelValue } = useCurrentModal()\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\" title=\"Modal Route\"\u003e\n    \u003cp\u003e Hello World\u003c/p\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\nIn `Index.vue`, just like how you use `\u003cRouterView\u003e` to render child routes,\nyou'll need to add `\u003cModalRouterView\u003e` in order to render the corresponding modal route.\n\n```vue\n\u003c!-- ./pages/Index.vue --\u003e\n\u003ctemplate\u003e\n  \u003cdiv\u003e\n    \u003ch1\u003eIndex\u003c/h1\u003e\n    \u003cModalRouterView /\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\nThat’s it — setup is complete!\nYou can now open the modal by navigating to the `/index-modal` route.\n\n### Modal Types\nIn `vue-modal-route`, there are three types of modals, each with different characteristics designed for specific use cases:\n\n- `Path`: Modals that are tied to a specific page and bound to a fixed URL.\n\n- `Global`: Modals that can be opened from any page, typically used for global features like login, preferences, etc. They do not have a fixed URL.\n\n- `Query`: Modals that can also be opened from any page, often used for functional dialogs like alerts or confirmations. These are triggered using specific query strings.\n\nIn the example above, we demonstrated a path modal, which is associated with a fixed URL.\n\n### Programmatically Open / Close Modal\nYou can use `useModalRoute` to open, close, or configure a modal route — from any component, not just the parent.\n\nTo interact with a modal route, you must reference it by its route name.\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nimport { useModalRoute } from '@vmrh/core'\n\nconst { openModal, closeModal } = useModalRoute()\n\nopenModal('modal-name') // use name of the route (e.g `MyIndexModal`)\n\ncloseModal('modal-name')\n\n\u003c/script\u003e\n```\n\n### Props / Data\nTo pass props to a modal route component, provide a `data` object when calling `openModal`.\n\n```ts\nopenModal('modal-name', {\n  data: {\n    message: \"Hi from parent.\"\n  }\n})\n```\n\nThen, receive it as `props` in the modal route component.\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nconst { modelValue } = useCurrentModal()\ndefineProps\u003c{\n  message?: string\n}\u003e()\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\"\u003e\n    \u003cdiv\u003eMessage: {{ message }} \u003c/div\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\n### `ReturnValue`\n`openModal` returns a promise that resolves when the modal is closed. The resolved value is the `returnValue`.\n\nBy default, the `returnValue` is null.\n\nThe modal route component can return a value using `closeAndReturn`. When this function is called, the modal will close, and the promise from `openModal` will resolve with the returned value.\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nconst { modelValue, closeAndReturn } = useCurrentModal()\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\"\u003e\n    \u003cdiv\u003eMessage: {{ message }} \u003c/div\u003e\n    \u003c!-- returnValue will be 'Modal returnValue' --\u003e\n    \u003cbutton @click=\"closeAndReturn('Modal returnValue')\"\u003eClose with value\u003c/button\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\n```ts\nconst returnValue = await openModal('modal-name')\n\nreturnValue // 'Modal returnValue'\n```\n\n### Params / Hash / Query\nYou can pass `params`, `query`, and `hash` to `openModal`, which will be used by `router.push`. This is particularly useful when your modal route path is dynamic. For example:\n\n```ts\n// dynamic route modal\n{\n  name: 'modal-name',\n  path: 'modal-name/:foo',\n  component: () =\u003e import('./pages/ModalName.vue'),\n  meta: {\n    modal: true,\n  },\n}\n```\n\n```ts\nopenModal('modal-name', {\n  params: { foo: 'bar' },\n  query: /** ... */,\n  hash:  /** ... */,\n})\n```\n\n### `setupModal`\nThe `setupModal` function is used to configure **child route modals**. It allows you to define the modal's `slot`, `props`, and initialization strategy.\n\nTo set this up, simply call `setupModal` in the parent component.\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nconst {\n  // Works like `openModal`, but only open `modal-name` you specified\n  open, \n  // Works like `closeModal`, but only close `modal-name` you specified\n  close,\n  // Computed object. returnValue of `modal-name` you specified\n  returnValue\n} = setupModal('modal-name')\n\n\nopen({ \n  data: {},\n  params: {},\n  // ...\n}) \n\u003c/script\u003e\n\u003ctemplate\u003e\n  \u003cModalRouterView /\u003e\n\u003c/template\u003e\n```\n\n\n#### `Props`\nSimilar to the `data` parameter in the `openModal` function, you can pass `props` to `setupModal` to define the `props` for the modal route component.\n\n```ts\nconst { open } = setupModal('modal-name', {\n  props: {\n    foo: 'bar'\n  }\n})\n```\n\nThe `data` will be merged into `props`, **with `data` taking precedence over props**.\n\n```ts\nconst { open } = setupModal('modal-name', {\n  props: {\n    foo: 'bar'\n  }\n})\nopen({\n  data: {\n    foo: 'bar2'\n  }\n})\n\n// The final props will be { foo: 'bar2' }\n```\n\nAdditionally, `props` can accept a function that receives the data passed into `openModal`, allowing you to manually merge them.\n\n```ts\nconst { open } = setupModal('modal-name', {\n  props(data) {\n    return {\n      message: data.message ?? 'default message'\n    }\n  }\n})\n```\n\n`props` can return `ref`, `computed` or `reactive`.\n\n```ts\nconst msg = ref('default message')\nconst reactiveObj = reactive({\n  message: 'def message',\n  name: 'name'\n})\n\nconst { open } = setupModal('modal-name', {\n  props(data) {\n    // Props will updated when msg.value changed\n    return {\n      message: msg.value\n    }\n  },\n\n  // Props will updated when reactiveObj changed\n  props: reactiveObj,\n})\n```\n\n\n#### `slots`\nYou can pass `slots` in two ways:\n\n1. Through the slots property in `setupModal`.\n2. By inserting them directly into the `ModalRouterView` slots.\n\nFor example, the modal route component might have a `custom` slot.\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nconst { modelValue } = useCurrentModal()\n\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\"\u003e\n    \u003cslot name=\"custom\" :visible=\"modelValue\" /\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\nThe `slots` property accepts a function that returns a vnode (similar to usage with the Vue `h` function).\n\n\nYou can insert the `custom` slot via `setupModal`. \n\n```ts\nsetupModal('modal-name', {\n  slots: {\n    custom: ({ visible }) =\u003e h('div', `custom message: ${visible}`)\n  }\n})\n```\n\nAlternatively, you can insert slots directly from `\u003cModalRouterView\u003e`. To specify the slot, use the `modal-name-[slot-name]` format.\n\n\n```vue\n\u003ctemplate\u003e\n    \u003cModalRouterView\u003e\n      \u003ctemplate #modal-name-custom=\"{ visible }\"\u003e\n        \u003cdiv\u003ecustom message {{ visible }}\u003c/div\u003e\n      \u003c/template\u003e\n    \u003c/ModalRouterView\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\nWhen both `setupModal` and `ModalRouterView` define the same slot name, the one in `setupModal` takes precedence.\n\n### Preparing Data Before Modal Open\nModal routes can be opened from another page. In such cases, you might need to prepare data before the modal opens, such as fetching data.\n\nYou can use `setupModal` and set `manual` option to `true` to prevent modal from opening immediately. \n\nFor example, consider opening the modal route `/user/info`, which is a child route of `/user`, from the homepage `/`.\n\n```ts\n// homepage `/`\n\nopenModal(`UserInfo`)\n```\n\nIn `/user`, you may want to prepare data before the modal opens and display it once the data is ready. Here’s how you can do it:\n\n```ts\nconst userMeta = ref({\n  authorized: false\n})\nconst { open } = setupModal('UserInfo', {\n  props: userMeta\n})\n\nonMouted(() =\u003e {\n  fetchUserMeta().then(res =\u003e {\n    userMeta.value.authorized = res.authorized\n  })\n})\n```\n\nTo prevent the modal from opening before the data is fetched, pass the option `manual: true` to `setupModal`.\n\n```ts\nconst { open } = setupModal('UserInfo', {\n  manual: true, // \u003c-- prevent modal from opening\n  props: userMeta,\n})\n```\n\nThen, call `unlock` after the data is ready, and the modal will open.\n\n```ts\nconst { open, unlock } = setupModal('UserInfo', {\n  manual: true, // \u003c-- prevent modal from opening\n  props: userMeta,\n})\n\nonMouted(() =\u003e {\n  fetchUserMeta().then(res =\u003e {\n    userMeta.value.authorized = res.authorized\n    unlock() // modal show up when `unlock` called\n  })\n})\n```\n\n### Route Setup\nTo setup a route for a modal route, for example:\n\n```ts\nexport const router = createModalRoute({\n  routes: [\n    {\n      name: \"Index\", // \u003c-- Base route\n      path: '/',\n      component: () =\u003e import(\"./pages/Index.vue\"),\n      children: [\n        {\n          name: \"MyIndexModal\", // \u003c-- Modal route\n          path: 'index-modal',\n          component: () =\u003e import(\"./pages/IndexModal.vue\"),\n          meta: {\n            modal: true,\n          }\n        }\n      ]\n    }\n  ]\n})\n```\n\nA route will be treated as a modal route if it satisfies the following conditions:\n\n1. It has `name` (string)\n2. It has a `component` or `components.default`\n3. It has `meta.modal: true` \n\n#### Base Route\nA modal route must have a **base route**. In the example above, the base route is `Index`.\n\nThe **base route** is required because, when the modal is closed, the system needs a route to navigate back to. Which is **base route**.\n\nThe base route must have a `component` or `components.default` defined to display content when the modal is not open.\n\n#### Route Must Have a Name\nCurrently, modal routes heavily rely on the route name for navigation. Therefore, you must define a name for every route.\n\n### Allow / Disallow Direct Access from URL\nBy default, modal routes do not allow direct access via URL.\n\nTo enable direct access, add `direct: true` to the route’s meta.\n\n```ts\nexport const router = createModalRoute({\n  routes: [\n    {\n      name: \"Index\", // \u003c-- Base route. If MyIndexModal does not allow directly access, user will be redirected to here.\n      path: '/',\n      component: () =\u003e import(\"./pages/Index.vue\"),\n      children: [\n        {\n          name: \"MyIndexModal\",\n          path: 'index-modal',\n          component: () =\u003e import(\"./pages/IndexModal.vue\"),\n          meta: {\n            modal: true,\n            direct: true, // \u003c--- This allow accessing from url\n          }\n        }\n      ]\n    }\n  ]\n})\n```\n\nIf direct access is not enabled for a modal route, attempting to navigate to its URL and hitting enter will redirect you to its** base route** (which is `Index` in this example). If the **base route is also a modal route** that disallows direct access, you will be redirected again to its own base route, and so on.\n\n### Global Modal\nA global modal route works similarly to a path modal, except that it can be displayed on any page without transitioning to another page.\n\nThe most common use case for a global modal is a `login` modal.\n\n#### Setup Global Modal\nTo set up a global modal, pass the routes to the `global` option in `createModalRoute`.\n\n```ts\nexport const router = createModalRoute({\n  routes: [\n    // ....\n  ],\n  global: [\n    {\n      name: 'Login',\n      path: 'login',\n      component: () =\u003e import('~/components/Login.vue'),\n      meta: {\n        modal: true,\n      },\n    },\n  ],\n})\n```\n\nThen, place `\u003cModalGlobalView\u003e` outside of `\u003cRouterView\u003e`, typically at the root of the component tree, such as in `\u003cApp\u003e`\n\n```vue\n\u003c!-- App.vue --\u003e\n\u003ctemplate\u003e\n    \u003cmain\u003e\n      \u003cRouterView /\u003e\n    \u003c/main\u003e\n    \u003cModalGlobalView /\u003e\n\u003c/template\u003e\n```\nThe global modal route component functions similarly to a path modal route component. For example:\n\n```vue\n\u003c!-- Login.vue --\u003e\n\u003cscript setup lang=\"ts\"\u003e\nconst { modelValue } = useCurrentModal()\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\"\u003e\n    \u003ch2\u003eLogin\u003c/div\u003e\n    \u003cLoginForm /\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\nThat's it, you can now open the login modal from anywhere.\n\n```vue\n\u003c!-- /some/path/any --\u003e\n\u003cscript setup lang=\"ts\"\u003e\nconst onLoginButtonClick = () =\u003e {\n  openModal('Login')\n}\n\u003c/script\u003e\n```\n\n#### Path of Global Modal\nThe global modal route path will be prefixed with `_modal` and appended to the current path. For example, if the current path is `/user/info` and you open a global modal route with the path `/login`, the resulting path will be `/user/info/_modal/login`.\n\n### Query Modal\nSimilar to global modals, query modals can be opened from any page without changing the page. The key differences between query modals and global modals are:\n\n1. Query modals open and close with changing the query string.\n2. Query modals cannot have child views.\n3. Query modals cannot be accessed directly via URL; they must be opened using `openModal` or `open` from `setupModal`.\n\nQuery modals are commonly used for utility purposes, such as alerts and confirmation dialogs.\n\n#### Setting Up a Query Modal\nTo set up a query modal, pass the routes to the `query` option in `createModalRoute`.\n\n```ts\nexport const router = createModalRoute({\n  routes: [\n    // ...\n  ],\n  query: [\n    {\n      name: 'Alert',\n      component: () =\u003e import('~/components/Alert.vue'),\n    },\n    {\n      name: 'Confirm',\n      component: () =\u003e import('~/components/Confirm.vue'),\n    },\n  ]\n})\n```\n\nThen place `\u003cModalQueryView\u003e` outside of `\u003cRouterView\u003e`, typically at the root of the component tree, such as in `\u003cApp\u003e`.\n\n```vue\n\u003c!-- App.vue --\u003e\n\u003ctemplate\u003e\n    \u003cmain\u003e\n      \u003cRouterView /\u003e\n    \u003c/main\u003e\n    \u003cModalQueryView /\u003e\n\u003c/template\u003e\n```\n\nThe query modal route component functions similarly to the path modal route component. For example:\n\n```vue\n\u003c!-- Confirm.vue --\u003e\n\u003cscript setup lang=\"ts\"\u003e\nconst { modelValue, closeAndReturn } = useCurrentModal()\n\ndefineProps\u003c{\n  title?: string,\n  message?: string\n}\u003e()\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\" :title=\"title\"\u003e\n    \u003cp\u003e{{ message }}\u003c/p\u003e\n    \u003cbutton @click=\"closeAndReturn(false)\"\u003e Cancel \u003c/button\u003e\n    \u003cbutton @click=\"closeAndReturn(true)\"\u003e Confirm \u003c/button\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\nThat's it, you can now open the Confirm modal from anywhere.\n\n```vue\n\u003c!-- /some/path/any --\u003e\n\u003cscript setup lang=\"ts\"\u003e\nconst onSubmit = () =\u003e {\n  const yes = await openModal('Confirm', {\n    data: {\n      title: \"Notice\",\n      message: \"Are you sure to submit the form ?\"\n    }\n  })\n  if (yes) {\n    // do something ...\n  }\n}\n\u003c/script\u003e\n```\n\n### Child Views in Modal Route Component\nOne of the key benefits of modal routes is that we can leverage the full power of Vue Router's `router-view` inside the modal.\n\nYou can register a route as a child route of the modal route.\n\n```ts\nexport const router = createModalRoute(\n  {\n    routes: [\n      {\n        name: \"Index\",\n        path: '/',\n        component: () =\u003e import(\"./pages/Index.vue\"),\n        children: [\n          {\n              name: 'User',\n              path: 'user',\n              component: () =\u003e import('./pages/user.vue'),\n              meta: {\n                modal: true\n              },\n              children: [\n                {\n                  name: \"Info\",\n                  path: 'info',\n                  component: () =\u003e import('./pages/user/info.vue'),\n                },\n                {\n                  name: \"Photos\",\n                  path: 'photos',\n                  component: () =\u003e import('./pages/user/photos.vue'),\n                },\n              ]\n            }\n        ]\n      }\n     \n    ]\n  }\n  \n)\n```\n\nTo render a child view within a modal route component, you can use `\u003cModalRouterView\u003e`.\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nimport { ModalRouterView } from '@vmrh/core'\n\nconst { modelValue } = useCurrentModal()\n\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\"\u003e\n    \u003c!-- ... --\u003e\n\n    \u003cModalRouterView /\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\n`\u003cModalRouterView\u003e` can be used just like `\u003cRouterView\u003e`\n\n```vue\n\u003ctemplate\u003e\n  \u003cModal v-model=\"visible\"\u003e\n    \u003c!-- ... --\u003e\n\n    \u003cRouterLink :to=\"{ name: 'Info' }\"\u003e\n      Go to Info\n    \u003c/RouterLink\u003e\n    \u003cRouterLink :to=\"{ name: 'Photos' }\"\u003e\n      Go to Photos\n    \u003c/RouterLink\u003e\n      \n    \u003cModalRouterView\u003e\n      \u003ctemplate #default=\"{ Component }\"\u003e\n        \u003cTransition name=\"fade\" mode=\"out-in\"\u003e\n            \u003ccomponent :is=\"Component\" /\u003e\n        \u003c/Transition\u003e\n      \u003c/template\u003e  \n    \u003c/ModalRouterView\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\nWhen you want to render a nested modal route, for example:\n\n```ts\nconst routes = createModalRoute(\n  {\n    routes: [\n      {\n        name: \"Index\",\n        path: '/',\n        component: () =\u003e import(\"./pages/Index.vue\"),\n        children: [\n          {\n              name: 'User',\n              path: 'user',\n              component: () =\u003e import('./pages/user.vue'),\n              meta: {\n                modal: true\n              },\n              children: [\n                {\n                  name: \"UserEdit\",\n                  path: 'edit',\n                  component: () =\u003e import('./pages/user/edit.vue'),\n                  meta: {\n                    modal: true\n                  }\n                },\n              ]\n            }\n        ]\n      }\n     \n    ]\n  }\n)\n```\n\nJust like rendering any other modal route, use `\u003cModalRouterView\u003e`, instead of `\u003cRouterView\u003e`.\n\n```vue\n\u003c!-- ./pages/user.vue --\u003e\n\u003cscript setup lang=\"ts\"\u003e\nimport { ModalRouterView } from '@vmrh/core'\n\nconst { modelValue } = useCurrentModal()\n\n\u003c/script\u003e\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\"\u003e\n    \u003c!-- ... --\u003e\n\n    \u003cModalRouterView /\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\n### Modal Layout\nThe modal you've chosen might not be the easiest to set up...\n\nIf you have a lot of modals, even simple configurations can quickly become overwhelming.\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nconst { modelValue } = useCurrentModal()\n\n\u003c/script\u003e\n\u003ctemplate\u003e\n  \u003cModal v-model=\"modelValue\"\u003e\n    \u003c!-- ... --\u003e\n  \u003c/Modal\u003e\n\u003c/template\u003e\n```\n\nTo simplify this setup, you can use `layouts`.\n\nStart by creating a modal layout component.\n\n```ts\n// ./src/modal/layout/default.vue\n\nimport { defineComponent, h, resolveComponent } from \"vue\";\nimport { useCurrentModal } from \"@vmrh/core\";\nimport Modal from \"./path-to-my-modal\"\n\nexport default defineComponent({\n  setup(props, { slots }) {\n    const { modelValue } = useCurrentModal()\n\n    return () =\u003e h(Modal, {\n      modelValue: modelValue.value,\n      'onUpdate:modelValue': (value: boolean) =\u003e modelValue.value = value,\n      ...props,\n    }, slots)\n  },\n})\n```\n\nThen, register these layouts in `createModalRoute`, where the keys represent the layout names.\n\n```ts\nimport { createModalRoute } from '@vmrh/core'\nimport { defineAsyncComponent } from 'vue'\n\nexport const router = createModalRoute({\n  layout: {\n    default: defineAsyncComponent(() =\u003e import('~/modal/layout/default')),\n    other: defineAsyncComponent(() =\u003e import('~/modal/layout/other')),\n    // default: LayoutDialog,\n  },\n\n  routes: [\n    // ...\n  ]\n})\n```\n\nWith this setup, whenever you want to use a modal, you can simply use `\u003cModalLayout\u003e`. By default, it will use the component registered under `layout.default`.\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\n// ...\n\u003c/script\u003e\n\u003ctemplate\u003e\n  \u003cModalLayout\u003e\n    \u003c!-- ... --\u003e\n  \u003c/ModalLayout\u003e\n\u003c/template\u003e\n```\n\nIf you want to use a different modal layout, just pass a different value to the `layout` prop, and it will apply the corresponding modal.\n\n```vue\n\u003ctemplate\u003e\n  \u003cModalLayout layout=\"other\"\u003e\n    \u003c!-- ... --\u003e\n  \u003c/ModalLayout\u003e\n\u003c/template\u003e\n```\n\n## Opening Multiple Types of Modals at the Same Time\nModals in `vue-modal-route` are categorized into three types: `path`, `global`, and `query`.\n\nThese modals can be opened simultaneously. For example, a path modal might be active, and then a global modal is opened on top of it. Or both a global modal and a query modal are open, and a path modal is triggered afterward. In such cases, `vue-modal-route` will handle the modal layers according to the following rules:\n\n### Priority order: `path \u003e global \u003e query`\n\n1. When a higher-priority modal is opened, all lower-priority modals are automatically closed.\n2. When a lower-priority modal is opened, the URL will be appended, preserving the higher-priority modal.\n3. (Optional, depending on modal implementation) Lower-priority modals are typically visually stacked in front of higher-priority modals.\n\n#### Case 1:\n1. A global modal is opened: URL becomes `/user/_modal/login`\n\n2. Then a query modal is opened: URL updates to `/user/_modal/login?m-confirm=`\n\n3. A path modal is then opened at `/products/:id/edit`: the URL becomes `/products/:id/edit`, and both the global and query modals are closed.\n\n#### Case 2:\n1. A path modal is opened: URL is `/products/:id/edit`\n\n2. A global modal is opened: URL becomes `/products/:id/edit/_modal/login`\n\n3. Then a query modal is opened: URL becomes `/products/:id/edit/_modal/login?m-confirm=`\n\n4. Another global modal is opened with path `/_modal/preference`: URL becomes ``/products/:id/edit/_modal/preference`. The previously opened login and query modals are closed.\n\n## Default Behavior\nBy default, `vue-modal-route` behaves similarly to a traditional modal, with the added benefit of being able to close the modal using the browser’s back button or navigation history.\n\n### Opening via Forward Navigation is Not Supported\nWhile users can go back to close the modal, they cannot navigate forward (e.g., using the \"Forward\" button) to open it.\n\nThis is based on the assumption that users are more likely to exit a modal than to re-enter it through forward navigation. In cases where users do want to re-open a modal, they usually do so via buttons or links. Furthermore, implementing forward navigation would require keeping track of modal state and data, which increases complexity. Given these trade-offs, `vue-modal-route` does not support forward navigation to open a modal.\n\n### Direct Access via URL is Disabled by Default\nBy default, modals cannot be accessed directly by URL. To enable direct access, you must explicitly set `meta.direct: true` in the route definition.\n\nAllowing direct access can greatly increase complexity in certain scenarios—especially when API calls or validations are required before opening the modal.\n\nFor example, consider a modal that shows detailed form submission results. This modal should only appear after a successful form submission and validation. If this modal could be directly accessed by URL, it would be difficult to ensure the required form data exists, forcing additional logic to handle such cases. In many scenarios, there’s no meaningful reason to allow direct access to such modals.\n\nFor these reasons, direct URL access is disabled by default.\n\n## License\nMIT\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshunnnet%2Fvue-modal-route","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshunnnet%2Fvue-modal-route","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshunnnet%2Fvue-modal-route/lists"}