Skip to content

Modal ​

A popup window for showing messages or gathering user input.

Usage ​

Use a Button or any other component in the default slot of the Modal.

Then, use the #content slot to add the content displayed when the Modal is open.

vue
<template>
  <B24Modal>
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="h-40 -m-[10px]" />
    </template>
  </B24Modal>
</template>

You can also use the #header, #body and #footer slots to customize the Modal's content.

Title ​

Use the title prop to set the title of the Modal's header.

Details
vue
<script setup lang="ts">
export interface ExampleProps {
  title?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!'
})
</script>

<template>
  <B24Modal
    :title="title"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
  </B24Modal>
</template>

Description ​

Use the description prop to set the description of the Modal's header.

Details
vue
<script setup lang="ts">
export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :title="title"
    :description="description"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
  </B24Modal>
</template>

Close ​

Use the close prop to customize or hide the close button (with false value) displayed in the Modal's header.

You can pass any property from the Button component to customize it.

TIP

The close button is not displayed if the #content slot is used as it's a part of the header.

Details
vue
<script setup lang="ts">
export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :title="title"
    :description="description"
    :close="{ color: 'air-primary-alert', size: 'lg', rounded: false }"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
  </B24Modal>
</template>

Close Icon ​

Use the close-icon prop to customize the close button @bitrix24/b24icons. Defaults to Cross30Icon.

Details
vue
<script setup lang="ts">
import EyeClosedIcon from '@bitrix24/b24icons-vue/button/EyeClosedIcon'

export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :title="title"
    :description="description"
    :close-icon="EyeClosedIcon"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
  </B24Modal>
</template>

Overlay ​

Use the overlay prop to control whether the Modal has an overlay or not. Defaults to true.

Details
vue
<script setup lang="ts">
export interface ExampleProps {
  isOverlay?: boolean
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  isOverlay: true,
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :overlay="isOverlay"
    :title="title"
    :description="description"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
  </B24Modal>
</template>

If you want to disable background blur, you should use the overlayBlur prop. The overlayBlur prop has 3 options:

  • auto: (default) when the user has not requested reduced motion
  • on: always use blur
  • off: do not use blur
Details
vue
<script setup lang="ts">
import type { ModalProps } from '@bitrix24/b24ui-nuxt'

export interface ExampleProps {
  overlayBlur?: ModalProps['overlayBlur']
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  overlayBlur: 'auto' as ModalProps['overlayBlur'],
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :overlay-blur="overlayBlur"
    :title="title"
    :description="description"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
  </B24Modal>
</template>

Transition ​

Use the transition prop to control whether the Modal is animated or not. Defaults to true.

INFO

Reduced movement is taken into account

Details
vue
<script setup lang="ts">
export interface ExampleProps {
  isTransition?: boolean
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  isTransition: true,
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :transition="isTransition"
    :title="title"
    :description="description"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
  </B24Modal>
</template>

Fullscreen ​

Use the fullscreen prop to make the Modal fullscreen.

Details
vue
<script setup lang="ts">
export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    fullscreen
    :title="title"
    :description="description"
    :b24ui="{ body: 'max-h-[calc(100vh-90px)]' }"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-[3000px]" />
    </template>
  </B24Modal>
</template>

Examples ​

Control open state ​

You can control the open state by using the default-open prop or the v-model:open directive.

INFO

In this example, leveraging defineShortcuts, you can toggle the Modal by pressing O.

TIP

This allows you to move the trigger outside of the Modal or remove it entirely.

Details
vue
<script setup lang="ts">
import { ref } from 'vue'

export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})

const open = ref(false)

defineShortcuts({
  o: () => open.value = !open.value
})
</script>

<template>
  <B24Modal
    v-model:open="open"
    :title="title"
    :description="description"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
  </B24Modal>
</template>

Disable dismissal ​

Set the dismissible prop to false to prevent the Modal from being closed when clicking outside of it or pressing escape. A close:prevent event will be emitted when the user tries to close it.

Details
vue
<script setup lang="ts">
export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :dismissible="false"
    :title="title"
    :description="description"
  >
    <B24Button label="Open" color="link" depth="dark" />

    <template #body>
      <ProseP accent="less-more" small>
        Modal non-dismissible: <ProseCode color="air-primary-copilot">Esc</ProseCode> also doesn't work
      </ProseP>
    </template>
  </B24Modal>
</template>

Programmatic usage ​

You can use the useOverlay composable to open a Modal programmatically.

WARNING

Make sure to wrap your app with the App component which uses the OverlayProvider component.

First, create a modal component that will be opened programmatically:

vue
<script setup lang="ts">
defineProps<{
  count: number
}>()

const emit = defineEmits<{ close: [boolean] }>()
</script>

<template>
  <B24Modal
    :close="{ onClick: () => emit('close', false) }"
    :title="`This modal was opened programmatically ${count} times`"
  >
    <template #footer>
      <div class="flex flex-row gap-[10px]">
        <B24Button
          color="air-primary-success"
          label="Success"
          @click="emit('close', true)"
        />
        <B24Button
          color="air-tertiary"
          label="Close"
          @click="emit('close', false)"
        />
      </div>
    </template>
  </B24Modal>
</template>

INFO

We are emitting a close event when the modal is closed or dismissed here. You can emit any data through the close event, however, the event must be emitted in order to capture the return value.

Then, use it in your app:

TIP

You can close the modal within the modal component by emitting emit('close').

Details
vue
<script setup lang="ts">
import { ref } from 'vue'
import LazyModal from './LazyModal.vue'

const count = ref(0)

const toast = useToast()
const overlay = useOverlay()

const modal = overlay.create(LazyModal)

async function open() {
  const instance = modal.open({
    count: count.value
  })

  const shouldIncrement = await instance.result

  if (shouldIncrement) {
    count.value++

    toast.add({
      title: `Success: ${shouldIncrement}`,
      color: 'air-primary-success',
      id: 'modal-success'
    })

    // Update the count
    modal.patch({
      count: count.value
    })
    return
  }

  toast.add({
    title: `Dismissed: ${shouldIncrement}`,
    color: 'air-primary-alert',
    id: 'modal-dismiss'
  })
}
</script>

<template>
  <B24Button label="Open" @click="open" />
</template>

Nested modals ​

You can nest modals within each other.

Details
vue
<script setup lang="ts">
import { ref } from 'vue'

export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})

const first = ref(false)
const second = ref(false)
</script>

<template>
  <B24Modal
    v-model:open="first"
    :title="title"
    :description="description"
  >
    <B24Button label="Open" />

    <template #footer>
      <div class="flex flex-row gap-[10px]">
        <B24Modal
          v-model:open="second"
          title="Second modal"
        >
          <B24Button
            color="air-primary"
            label="Open second"
          />

          <template #footer>
            <B24Button
              color="air-tertiary"
              label="Close"
              @click="second = false"
            />
          </template>
        </B24Modal>
        <B24Button
          color="air-tertiary"
          label="Close"
          @click="first = false"
        />
      </div>
    </template>
  </B24Modal>
</template>

Use the #footer slot to add content after the Modal's body.

TIP

You can also close the dialog box using the B24ModalDialogClose component.

Details
vue
<script setup lang="ts">
export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :title="title"
    :description="description"
    :b24ui="{ footer: 'flex-row-reverse justify-start' }"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-40" />
    </template>
    <template #footer="{ close }">
      <B24Button
        label="Cancel"
        color="air-tertiary"
        @click="close"
      />
      <B24ModalDialogClose>
        <B24Button label="Send" color="air-primary" />
      </B24ModalDialogClose>
    </template>
  </B24Modal>
</template>

With body slot ​

Use the #body slot to add content.

Details
vue
<script setup lang="ts">
import FileUploadIcon from '@bitrix24/b24icons-vue/main/FileUploadIcon'

export interface ExampleProps {
  title?: string
  description?: string
}

withDefaults(defineProps<ExampleProps>(), {
  title: 'Heads up!',
  description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>

<template>
  <B24Modal
    :title="title"
    :description="description"
    :b24ui="{ footer: 'border-t-0 mt-2.5 pt-0' }"
  >
    <B24Button label="Open" />

    <template #body>
      <div class="flex flex-col gap-[14px]">
        <B24Separator />
        <div class="w-full flex flex-row flex-nowrap items-center justify-start gap-3">
          <div class="size-8xl rounded-xs border border-(--ui-color-divider-default) flex flex-col items-center justify-center">
            <FileUploadIcon class="size-10 text-(--b24ui-typography-description-color)" />
          </div>
          <div class="flex flex-col flex-nowrap gap-1">
            <ProseH5 class="mb-0">
              start-ui.md
            </ProseH5>
            <ProseP small accent="less-more">
              650 bytes
            </ProseP>
          </div>
        </div>
        <B24Separator />
        <div class="w-full mt-[6px]">
          <B24Textarea autofocus placeholder="Add a comment" autoresize :rows="1" :maxrows="4" />
        </div>
      </div>
    </template>
    <template #footer>
      <div class="flex flex-row gap-[10px]">
        <B24ModalDialogClose>
          <B24Button label="Send" color="air-primary" />
        </B24ModalDialogClose>
        <B24ModalDialogClose>
          <B24Button label="Cancel" color="air-tertiary" />
        </B24ModalDialogClose>
      </div>
      <div>
        <B24Button label="Full version" size="sm" color="air-tertiary-no-accent" />
      </div>
    </template>
  </B24Modal>
</template>

API ​

Props ​

Prop Default Type
titlestring
descriptionstring
contentOmit<DialogContentProps, "asChild" | "as" | "forceMount"> & Partial<EmitsToProps<DialogContentImplEmits>>
The content of the modal
overlaytrueboolean
Render an overlay behind the modal.
overlayBlur"auto""auto" | "on" | "off"
Render an overlay blur behind the modal. `auto` use `motion-safe`.
transitiontrueboolean
Animate the modal when opening or closing.
fullscreenfalseboolean
When `true`, the modal will take up the full screen.
portaltruestring | false | true | HTMLElement
Render the modal in a portal.
closetrueboolean | Partial<ButtonProps>
Display a close button to dismiss the modal. `{ size: 'xs', color: 'link' }`{lang="ts"}
closeIconicons.close(props: HTMLAttributes & VNodeProps & {}, ctx: Omit<{ attrs: Data; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<...>>(exposed?: Exposed) => void; }, "expose">): any
The icon displayed in the close button.
dismissibletrueboolean
When `false`, the modal will not close when clicking outside or pressing escape.
scrollbarThintrueboolean
openboolean
The controlled open state of the dialog. Can be binded as `v-model:open`.
defaultOpenboolean
The open state of the dialog when it is initially rendered. Use when you do not need to control its open state.
modaltrueboolean
The modality of the dialog When set to `true`, <br> interaction with outside elements will be disabled and only dialog content will be visible to screen readers.
b24ui{ overlay?: ClassNameValue; content?: ClassNameValue; contentWrapper?: ClassNameValue; header?: ClassNameValue; wrapper?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; close?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; }

Slots ​

Slot Type
default{ open: boolean; }
content{ close: () => void; }
header{ close: () => void; }
title{}
description{}
actions{}
close{ close: () => void; b24ui: { overlay: (props?: Record<string, any>) => string; content: (props?: Record<string, any> | undefined) => string; contentWrapper: (props?: Record<string, any> | undefined) => string; header: (props?: Record<string, any> | undefined) => string; wrapper: (props?: Record<string, any> | undefined) => string; title: (props?: Record<string, any> | undefined) => string; description: (props?: Record<string, any> | undefined) => string; close: (props?: Record<string, any> | undefined) => string; body: (props?: Record<string, any> | undefined) => string; footer: (props?: Record<string, any> | undefined) => string; }; }
body{ close: () => void; }
footer{ close: () => void; }

Emits ​

ts
/**
 * Emitted events for the Modal component
 */
interface ModalEmits {
  after:leave: (payload: []) => void;
  after:enter: (payload: []) => void;
  close:prevent: (payload: []) => void;
  update:open: (payload: [value: boolean]) => void;
}

Released under the MIT License.