Error page composition
Nuxt Maintainer does not replace error.vue. HTML navigation throws a tagged fatal 503 through Nuxt, retaining both the requested URL and the application's existing error dispatcher.
<script setup lang="ts">
const props = defineProps<{ error: unknown }>()
</script>
<template>
<NuxtMaintenanceError v-if="isMaintenanceError(error)" :error="error" />
<MyErrorPage v-else :error="error" />
</template>
Maintenance errors carry data.code === 'NUXT_MAINTAINER'. API and JSON requests receive a structured JSON 503. Retry-After and Refresh headers are included when configured.
Customize the presentation
NuxtMaintenanceError provides compound components for each part of the maintenance state. Add only the parts you want and arrange them in any order:
<script setup lang="ts">
const props = defineProps<{ error: unknown }>()
</script>
<template>
<NuxtMaintenanceError
v-if="isMaintenanceError(error)"
:error="error"
>
<main class="maintenance-page">
<NuxtMaintenanceErrorTitle>
<h1>We will be right back</h1>
</NuxtMaintenanceErrorTitle>
<NuxtMaintenanceErrorMessage v-slot="{ message }">
<p role="status">{{ message }}</p>
</NuxtMaintenanceErrorMessage>
<NuxtMaintenanceErrorSince v-slot="{ since }">
<p>Maintenance started at <time :datetime="since">{{ since }}</time>.</p>
</NuxtMaintenanceErrorSince>
<NuxtMaintenanceErrorRetryAfter v-slot="{ retryAfter }">
<p>Try again in {{ retryAfter }} seconds.</p>
</NuxtMaintenanceErrorRetryAfter>
<NuxtMaintenanceErrorRefresh />
</main>
</NuxtMaintenanceError>
<MyErrorPage v-else :error="error" />
</template>
The available children are NuxtMaintenanceErrorTitle, NuxtMaintenanceErrorMessage, NuxtMaintenanceErrorSince, NuxtMaintenanceErrorRetryAfter, and NuxtMaintenanceErrorRefresh. They must be rendered inside NuxtMaintenanceError. A child without a slot renders accessible default markup. Supplying a slot replaces that markup completely and exposes the corresponding raw value.
The zero-configuration component continues to show the title, message, and start time. Retry and refresh text is opt-in. These values are static seconds from the maintenance response; the components do not start countdowns or perform client-side refreshes.
Build the page yourself
Use useNuxtMaintenanceError when the component markup is not useful for your design. Pass a getter so the result follows changes to the error prop:
<script setup lang="ts">
const props = defineProps<{ error: unknown }>()
const { state, message, since, retryAfter, refresh } = useNuxtMaintenanceError(() => props.error)
</script>
<template>
<MyMaintenancePage
v-if="state"
:message="message"
:since="since"
:retry-after="retryAfter"
:refresh="refresh"
/>
<MyErrorPage v-else :error="error" />
</template>
The composable synchronously reads the state already embedded in the Nuxt error. It does not fetch maintenance status or add timer behavior.