diff --git a/packages/docs/src/examples/v-alert/prop-duration.vue b/packages/docs/src/examples/v-alert/prop-duration.vue
new file mode 100644
index 00000000000..4d7ee8f6d50
--- /dev/null
+++ b/packages/docs/src/examples/v-alert/prop-duration.vue
@@ -0,0 +1,25 @@
+
+
+
+ Show alert
+
+
+
+
+
+
+
diff --git a/packages/docs/src/pages/en/components/alerts.md b/packages/docs/src/pages/en/components/alerts.md
index 97701b717a5..7959420d393 100644
--- a/packages/docs/src/pages/en/components/alerts.md
+++ b/packages/docs/src/pages/en/components/alerts.md
@@ -131,6 +131,12 @@ The **closable** prop adds a [v-icon](/components/icons) on the far right, after
+#### Duration
+
+The **duration** prop causes the alert to automatically dismiss itself after the specified number of milliseconds. Set to `-1` (the default) to disable auto-dismiss. Combine with **closable** to give users both auto-dismiss and manual control.
+
+
+
The close icon automatically applies a default `aria-label` and is configurable by using the **close-label** prop or changing **close** value in your locale.
::: info
diff --git a/packages/vuetify/src/components/VAlert/VAlert.tsx b/packages/vuetify/src/components/VAlert/VAlert.tsx
index 42672ddc0e0..b30684c40d7 100644
--- a/packages/vuetify/src/components/VAlert/VAlert.tsx
+++ b/packages/vuetify/src/components/VAlert/VAlert.tsx
@@ -25,7 +25,7 @@ import { makeThemeProps, provideTheme } from '@/composables/theme'
import { genOverlays, makeVariantProps, useVariant } from '@/composables/variant'
// Utilities
-import { toRef } from 'vue'
+import { onMounted, onScopeDispose, toRef, watch } from 'vue'
import { genericComponent, propsFactory } from '@/util'
// Types
@@ -57,6 +57,10 @@ export const makeVAlertProps = propsFactory({
type: String,
default: '$vuetify.close',
},
+ duration: {
+ type: [Number, String],
+ default: -1,
+ },
icon: {
type: [Boolean, String, Function, Object] as PropType,
default: null,
@@ -107,6 +111,28 @@ export const VAlert = genericComponent()({
setup (props, { emit, slots }) {
const isActive = useProxiedModel(props, 'modelValue')
+
+ let dismissTimer = -1
+ function clearDismissTimer () {
+ if (dismissTimer !== -1) {
+ window.clearTimeout(dismissTimer)
+ dismissTimer = -1
+ }
+ }
+ function scheduleDismiss () {
+ clearDismissTimer()
+ const ms = Number(props.duration)
+ if (ms > 0 && isActive.value) {
+ dismissTimer = window.setTimeout(() => {
+ isActive.value = false
+ dismissTimer = -1
+ }, ms)
+ }
+ }
+ onMounted(scheduleDismiss)
+ watch(() => [props.duration, isActive.value], scheduleDismiss)
+ onScopeDispose(clearDismissTimer)
+
const icon = toRef(() => {
if (props.icon === false) return undefined
if (!props.type) return props.icon
diff --git a/packages/vuetify/src/components/VSkeletonLoader/VSkeletonLoader.sass b/packages/vuetify/src/components/VSkeletonLoader/VSkeletonLoader.sass
index d75b6de9aaa..e0c3e81f4f9 100644
--- a/packages/vuetify/src/components/VSkeletonLoader/VSkeletonLoader.sass
+++ b/packages/vuetify/src/components/VSkeletonLoader/VSkeletonLoader.sass
@@ -103,6 +103,27 @@
height: $skeleton-loader-chip-height
max-width: $skeleton-loader-chip-width
+ &__chip-group
+ flex-wrap: wrap
+
+ .v-skeleton-loader__chip
+ flex: 0 0 auto
+
+ &__timeline-item,
+ &__timeline-item-two,
+ &__timeline-item-three
+ flex-wrap: nowrap
+ align-items: flex-start
+
+ > .v-skeleton-loader__avatar
+ flex: 0 0 auto
+ align-self: center
+
+ > .v-skeleton-loader__heading,
+ > .v-skeleton-loader__sentences,
+ > .v-skeleton-loader__paragraph
+ flex: 1 1 auto
+
+ .v-skeleton-loader__bone
flex: 1 1 auto
margin-inline-start: 0
diff --git a/packages/vuetify/src/components/VSkeletonLoader/VSkeletonLoader.tsx b/packages/vuetify/src/components/VSkeletonLoader/VSkeletonLoader.tsx
index 17e9477de10..12a33414d06 100644
--- a/packages/vuetify/src/components/VSkeletonLoader/VSkeletonLoader.tsx
+++ b/packages/vuetify/src/components/VSkeletonLoader/VSkeletonLoader.tsx
@@ -28,6 +28,7 @@ export const rootTypes = {
card: 'image, heading',
'card-avatar': 'image, list-item-avatar',
chip: 'chip',
+ 'chip-group': 'chip@5',
'date-picker': 'list-item, heading, divider, date-picker-options, date-picker-days, actions',
'date-picker-options': 'text, avatar@2',
'date-picker-days': 'avatar@28',
@@ -52,6 +53,9 @@ export const rootTypes = {
'table-row': 'text@6',
'table-tfoot': 'text@2, avatar@2',
text: 'text',
+ 'timeline-item': 'avatar, heading',
+ 'timeline-item-two': 'avatar, sentences',
+ 'timeline-item-three': 'avatar, paragraph',
} as const
function genBone (type: string, children: VSkeletonBones = []) {