Skip to content

Custom Templates

While the built-in templates cover many common scenarios, Astro Maintenance v2.0 also allows you to create fully custom maintenance pages. You have two main approaches for customization:

  1. Custom Handlebars Templates: Create your own template files with full control over HTML, CSS, and available variables (requires ?raw imports in v2.0)
  2. Internal Route Redirection: Redirect to a custom Astro page in your project

Custom templates now require ?raw imports for universal serverless compatibility. File path-based templates are no longer supported.

You can create your own maintenance page template using Handlebars syntax. This gives you complete control over the appearance and functionality of your maintenance page.

v2.0 Method (Current):

To use a custom template in v2.0, you must import the template content using ?raw and pass it to the integration:

astro.config.mjs
import { defineConfig } from "astro/config";
import maintenance from "astro-maintenance";
// Import template content using ?raw
import customTemplate from "./templates/custom-maintenance.hbs?raw";
export default defineConfig({
integrations: [
maintenance({
enabled: true,
template: customTemplate, // Pass imported template content
title: "Custom Maintenance Page",
description: "Our site is getting an upgrade.",
// ... other options will be passed to the template
}),
],
});

v1.x Method (Deprecated):

// ❌ This no longer works in v2.0
maintenance({
template: "./templates/custom-maintenance.hbs", // File paths not supported
});

All configuration options you provide to the integration are available as variables in your Handlebars template. The most common ones include:

  • {{title}} - The page title
  • {{description}} - The description text
  • {{logo}} - Path to the logo image, if provided
  • {{emailAddress}} - Contact email address, if provided
  • {{emailText}} - Text before the email address, if provided
  • {{copyright}} - Copyright text, if provided
  • {{countdown}} - ISO date string for countdown, if provided

First, create your Handlebars template file (e.g., templates/custom-maintenance.hbs):

<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{title}}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background-color: #f5f5f5;
color: #333;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
max-width: 600px;
padding: 40px;
background-color: white;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
text-align: center;
}
img {
max-width: 200px;
margin-bottom: 20px;
}
h1 {
margin-top: 0;
color: #1e293b;
}
.description {
margin-bottom: 30px;
line-height: 1.6;
}
.contact {
margin-top: 20px;
font-size: 14px;
color: #64748b;
}
footer {
margin-top: 40px;
font-size: 12px;
color: #94a3b8;
}
</style>
</head>
<body>
<div class="container">
{{#if logo}}<img src="{{logo}}" alt="Logo" />{{/if}}
<h1>{{title}}</h1>
<div class="description">{{description}}</div>
{{#if emailAddress}}
<div class="contact">
{{emailText}}
<a href="mailto:{{emailAddress}}">{{emailAddress}}</a>
</div>
{{/if}}
{{#if copyright}}
<footer>{{copyright}}</footer>
{{/if}}
</div>
</body>
</html>

Here’s a complete example showing how to create and use a custom template in v2.0:

1. Create your template file (templates/custom-maintenance.hbs):

<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{title}}</title>
<style>
/* Your custom styles */
body {
font-family: system-ui, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
text-align: center;
max-width: 600px;
padding: 2rem;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.logo { max-width: 150px; margin-bottom: 1rem; }
h1 { font-size: 3rem; margin: 1rem 0; }
p { font-size: 1.2rem; opacity: 0.9; }
.contact { margin-top: 2rem; }
.social-links { margin: 2rem 0; }
.social-links a {
margin: 0 0.5rem;
color: white;
text-decoration: none;
opacity: 0.8;
}
.social-links a:hover { opacity: 1; }
</style>
</head>
<body>
<div class="container">
{{#if logo}}<img src="{{logo}}" alt="Logo" class="logo" />{{/if}}
<h1>{{title}}</h1>
<p>{{description}}</p>
{{#if socials}}
<div class="social-links">
{{#if socials.twitter}}<a href="{{socials.twitter}}" target="_blank">Twitter</a>{{/if}}
{{#if socials.github}}<a href="{{socials.github}}" target="_blank">GitHub</a>{{/if}}
{{#if socials.linkedin}}<a href="{{socials.linkedin}}" target="_blank">LinkedIn</a>{{/if}}
</div>
{{/if}}
{{#if emailAddress}}
<div class="contact">
{{emailText}} <a href="mailto:{{emailAddress}}" style="color: white;">{{emailAddress}}</a>
</div>
{{/if}}
{{#if copyright}}
<footer style="margin-top: 2rem; opacity: 0.7; font-size: 0.9rem;">{{copyright}}</footer>
{{/if}}
</div>
</body>
</html>

2. Import and use in your Astro config:

astro.config.mjs
import { defineConfig } from "astro/config";
import maintenance from "astro-maintenance";
import customTemplate from "./templates/custom-maintenance.hbs?raw";
export default defineConfig({
integrations: [
maintenance({
enabled: true,
template: customTemplate,
title: "We'll Be Back Soon!",
description: "Our website is getting a major upgrade with exciting new features.",
logo: "/logo.png",
emailAddress: "[email protected]",
emailText: "Questions? Contact us:",
copyright: "© 2025 Your Company",
socials: {
twitter: "https://twitter.com/yourhandle",
github: "https://github.com/yourusername",
linkedin: "https://linkedin.com/in/yourprofile",
},
override: "preview",
}),
],
});

If you want to include a countdown timer in your custom template, you’ll need to add JavaScript to handle the countdown logic. Here’s a simplified example:

<html lang="en">
<!-- Head content here -->
<body>
<div class="container">
<!-- Other content here -->
{{#if countdown}}
<div id="countdown">
<div id="days">00</div> days
<div id="hours">00</div> hours
<div id="minutes">00</div> minutes
<div id="seconds">00</div> seconds
</div>
<script>
// Convert countdown string to Date object
const countdownDate = new Date("{{countdown}}").getTime();
// Update the countdown every second
const timer = setInterval(() => {
const now = new Date().getTime();
const distance = countdownDate - now;
// Calculate days, hours, minutes, seconds
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
// Display the result
document.getElementById("days").textContent = days.toString().padStart(2, '0');
document.getElementById("hours").textContent = hours.toString().padStart(2, '0');
document.getElementById("minutes").textContent = minutes.toString().padStart(2, '0');
document.getElementById("seconds").textContent = seconds.toString().padStart(2, '0');
// If the countdown is finished, reload the page
if (distance < 0) {
clearInterval(timer);
setTimeout(() => {
window.location.reload();
}, 10000); // Try to reload every 10 seconds
}
}, 1000);
</script>
{{/if}}
</div>
</body>
</html>

If you prefer to use Astro components for your maintenance page, you can redirect to a custom route in your Astro project:

maintenance({
enabled: true,
template: "/custom-maintenance", // Redirects to this route in your Astro site
override: "preview",
})

This performs a 302 redirect to the specified route when maintenance mode is enabled. You’ll need to:

  1. Create a page at the specified route in your Astro project (e.g., /src/pages/custom-maintenance.astro)
  2. Build your maintenance page using Astro components, layouts, and styles

This approach lets you use all of Astro’s features for your maintenance page, including:

  • Astro components
  • Your site’s existing layouts
  • CSS frameworks or styling solutions
  • Client-side JavaScript when needed

If you’re upgrading from v1.x, you need to update your custom template usage:

❌ v1.x (deprecated):

maintenance({
template: "./templates/custom.hbs", // File path - no longer works
});

✅ v2.0 (required):

import customTemplate from "./templates/custom.hbs?raw";
maintenance({
template: customTemplate, // Imported content
});

When creating custom templates in v2.0:

  1. Import with ?raw: Always use the ?raw suffix when importing template files
  2. Keep it simple: Focus on clearly communicating maintenance status
  3. Make it responsive: Ensure your template works on all device sizes
  4. Include essential information: Always provide a title, description, and estimated completion time
  5. Branding: Include your logo and use your brand colors for consistency
  6. Contact information: Provide a way for users to get help during maintenance
  7. Universal compatibility: Your templates will work across all deployment platforms (Node.js, Cloudflare Workers, Vercel, Netlify)
  8. Testing: Test your custom template before deploying it to production

Now that you’ve learned about all the available templates, check out these resources: