Ever wanted to put up a sleek “Coming Soon” or “Under Maintenance” page while you prep your next big thing? Maybe you’ve got a new product launching, or your site is undergoing updates. Either way, creating a friendly, professional page can help manage expectations and keep your audience engaged.
Today, I’ll show you how to do precisely that using Next.js and the @heroui/react UI kit. We’ll create a stylish page with a countdown timer using Card and a custom Countdown component.
Let’s dive in!
Setting Up the Next.js Project

Creating the Page Layout
Let’s design the structure of the coming soon page.
Create a file: app/coming-soon/page.tsx
Here’s the basic layout:
"use client";
import { Card } from '@heroui/react';
import Countdown from '../../components/Countdown';
export default function CommingSoon() {
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
<Card className="max-w-md w-full p-8 shadow-xl">
<p className="mb-4 text-center">
We're Launching Soon 🚀
</p>
<p className="mb-6 text-center text-gray-600">
Our website is under maintenance. We're working hard to bring you a better experience.
</p>
<Countdown targetDate="2025-12-31T00:00:00" />
</Card>
</div>
);
}We’re using the Card component for a polished container. Inside, we place a heading and a short message using Typography Tailwind. The magic happens with the Countdown component, which we’ll build next.
Feel free to style this however you want! HeroUI gives you flexibility with utility classes and their props.
Creating the Countdown Component
Let’s now create our countdown timer.
Make a new file at components/Countdown.js:
import { useEffect, useState } from 'react';
type CountdownProps = {
targetDate: string | Date;
};
type TimeLeft = {
days: number;
hours: number;
minutes: number;
seconds: number;
};
export default function Countdown({ targetDate }: CountdownProps) {
const calculateTimeLeft = (): TimeLeft => {
const difference = new Date(targetDate).getTime() - new Date().getTime();
const timeLeft: TimeLeft = {
days: Math.floor(difference / (1000 * 60 * 60 * 24)),
hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
minutes: Math.floor((difference / 1000 / 60) % 60),
seconds: Math.floor((difference / 1000) % 60),
};
return timeLeft;
};
const [timeLeft, setTimeLeft] = useState<TimeLeft>(calculateTimeLeft());
useEffect(() => {
const timer = setInterval(() => {
setTimeLeft(calculateTimeLeft());
}, 1000);
return () => clearInterval(timer);
}, [targetDate]);
return (
<div className="grid grid-cols-4 gap-2 text-center">
{Object.entries(timeLeft).map(([unit, value]) => (
<div key={unit}>
<div className="text-2xl font-bold">{value}</div>
<div className="text-sm uppercase text-gray-500">{unit}</div>
</div>
))}
</div>
);
}This simple component takes a targetDate and displays days, hours, minutes, and seconds. It updates every second using setInterval.
Each time unit is styled with Tailwind. It’s readable, elegant, and works across devices.

Tips to Make It Shine
Now that your base is ready, let’s polish it up.
1. Make It Responsive
Use Tailwind’s responsive classes:
<Card className="w-full max-w-md sm:max-w-lg p-8 shadow-xl">
This makes the card adjust beautifully on small screens.

2. Add a Logo or Image
Add this before the heading:
"use client";
import { Card, CardBody, CardHeader, Image } from "@heroui/react";
import Countdown from "../../components/Countdown";
export default function CommingSoon() {
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
<Card className="w-full max-w-md sm:max-w-lg p-8 shadow-xl">
<CardHeader className="pb-0 pt-2 px-4 flex-col items-start py-2">
<p className="mb-4 text-center text-2xl font-bold text-gray-800">
We're Launching Soon 🚀
</p>
<p className="mb-6 text-center text-lg text-gray-600 leading-relaxed">
Our website is under maintenance. We're working hard to bring you a
better experience.
</p>
<Countdown targetDate="2025-12-31T00:00:00" />
</CardHeader>
<CardBody className="overflow-visible py-2">
<Image
alt="HeroUI hero Image"
src="https://heroui.com/images/hero-card-complete.jpeg"
width={300}
/>
</CardBody>
</Card>
</div>
);
}You can replace this with your brand’s logo or even a coming soon graphic.

3. SEO Optimization

4. Add a Contact or Notify Form
You can integrate a form using tools like Formspree or ConvertKit. A simple form might look like:
"use client";
import { Card, CardBody, CardHeader, Image } from "@heroui/react";
import Countdown from "../../components/Countdown";
import { useState } from "react";
export default function CommingSoon() {
const [email, setEmail] = useState("");
const [submitted, setSubmitted] = useState(false);
const handleSubmit = (e: any) => {
e.preventDefault();
console.log("Subscribing email:", email);
setSubmitted(true);
setEmail("");
};
return (
<div className="min-h-screen bg-gradient-to-br from-gray-100 via-blue-100 to-indigo-100 flex items-center justify-center py-10">
<Card className="w-full max-w-md sm:max-w-lg rounded-xl shadow-xl overflow-hidden bg-white/90 backdrop-blur-sm">
<CardHeader className="relative pt-10 pb-8 px-8 sm:px-16 flex justify-center">
<h2 className="mt-4 text-center text-3xl font-semibold text-gray-900">
Coming Soon
</h2>
<p className="mt-2 text-center text-gray-600 leading-relaxed font-light">
We're brewing something amazing. Stay tuned!
</p>
</CardHeader>
<CardBody className="p-8 sm:px-16 pb-8">
<div className="relative rounded-lg overflow-hidden shadow-md aspect-w-16 aspect-h-9 mb-6">
<Image
alt="Modern abstract graphic representing launch"
src="https://images.unsplash.com/photo-1518051870910-a46e30d9db16?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHwwfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2070&q=80"
className="object-cover w-full h-full"
/>
<div className="absolute inset-0 bg-black/10" aria-hidden="true" />
</div>
<div className="mb-8">
<Countdown
targetDate="2025-12-31T00:00:00"
/>
</div>
<div className="w-full text-center">
<h3 className="text-xl font-semibold text-gray-900 mb-4">
Get Notified
</h3>
<p className="text-gray-600 mb-6 font-light">
Be the first to know when we launch.
</p>
{!submitted ? (
<form
onSubmit={handleSubmit}
className="flex flex-col sm:flex-row gap-3"
>
<input
type="email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="flex-grow p-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-all duration-200"
/>
<button
type="submit"
className="w-full sm:w-auto px-6 py-3 bg-indigo-600 text-white font-semibold rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-all duration-200"
>
Subscribe
</button>
</form>
) : (
<p className="text-green-600 font-medium">
You're on the list! 🎉
</p>
)}
</div>
</CardBody>
</Card>
</div>
);
}

Finally
You’ve just built a fully functional Coming Soon page using Next.js and @heroui/react. You used Cards and Typography Tailwind to create a clean layout, and a custom Countdown component to give it purpose and urgency.
Now, whenever your site is under maintenance or you’re gearing up for a launch, you’re ready.
Don’t forget — branding matters. You can add your brand colors, fonts, and personal touches to make this page your own.
Feel free to bookmark this tutorial and come back to it when needed.
Building a Next.js Countdown “Coming Soon” Page
Project Setup & Page Layout
Goal: Establish the base Next.js page (e.g., `/coming-soon/page.tsx`).
Key Technologies: Next.js, @heroui/react Card component, and Tailwind CSS for initial centering and styling.
Create the Countdown Component
Goal: Calculate the remaining time in real-time.
Implementation: Create a reusable component (`Countdown.js`). Use React’s useState for time data and useEffect with setInterval(..., 1000) to re-calculate the difference every second until the `targetDate` is reached.
Render Time Units
Goal: Present the calculated time clearly (Days, Hours, Minutes, Seconds).
Implementation: Use a simple grid layout and map over the calculated time object to display each unit with bold numbers and descriptive labels, ensuring responsive styling with Tailwind.
Enhancements & SEO
Goal: Maximize impact and engagement before launch.
Polish: Ensure responsiveness across devices, add branding (logo/image). Engagement: Integrate an email subscribe form to capture leads. Visibility: Optimize for SEO.
This article was originally published on Medium.



