When running a production-grade React application served by Nginx, maintenance mode becomes a handy tool for planned downtime. I recently implemented maintenance mode for a Vite-powered React app and learned a lot about how to make it efficient and dynamic. Here, I’ll share the steps to set it up so that your app checks for maintenance mode periodically, ensuring minimal disruption to your users.
Thank me by sharing on Twitter 🙏
Why Maintenance Mode Matters
Maintenance mode is essential when you need to pause your app’s functionality for updates, data migrations, or server-side changes. Instead of users encountering errors or partially loaded pages, a simple maintenance page ensures they know the app will be back soon.
My setup leverages Nginx to dynamically signal maintenance mode through HTTP headers. The app periodically checks the server for updates, making it responsive to changes without a full page reload.
Setting Up Nginx for Maintenance Mode
To start, I configured Nginx to send a custom HTTP header, which serves as the maintenance mode indicator.
Here’s how I modified the Nginx configuration:
Co-Intelligence: Living and Working with AI
$17.79 (as of December 21, 2024 19:39 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)COLORCORAL Cleaning Gel Universal Dust Cleaner for PC Keyboard Car Detailing Office Electronics Laptop Dusting Kit Computer Dust Remover, Stocking Stuffers for Men Women Kids Teens 160g
$6.98 (as of December 21, 2024 08:38 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)HP 67XL Black High-yield Ink Cartridge | Works with HP DeskJet 1255, 2700, 4100 Series, HP ENVY 6000, 6400 Series | Eligible for Instant Ink | One Size | 3YM57AN
$29.89 (as of December 21, 2024 08:38 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)server {
listen 80;
server_name my-app.com;
location / {
# Serve the React app
root /path/to/vite/build;
index index.html;
# Add a custom header for maintenance mode
add_header X-Maintenance-Mode "true";
}
}
The add_header
directive lets me control whether maintenance mode is active by setting X-Maintenance-Mode
to "true"
or "false"
. Updating this header doesn’t require redeploying the app; I just reload the Nginx configuration.
To apply changes, I ran:
sudo nginx -s reload
With this setup, Nginx dynamically informs the app about the maintenance mode status.
Building a Custom Hook for Maintenance Checks
Next, I moved to the React app. Instead of hardcoding maintenance mode, I created a custom hook that fetches the X-Maintenance-Mode
header from the server. This hook checks the server every 60 seconds to detect updates dynamically.
Here’s the TypeScript code for the hook:
import { useState, useEffect } from 'react';
const useMaintenanceMode = (): boolean => {
const [isMaintenanceMode, setIsMaintenanceMode] = useState(false);
useEffect(() => {
const checkMaintenanceMode = async () => {
try {
const response = await fetch(window.location.href);
const maintenanceMode = response.headers.get('X-Maintenance-Mode');
setIsMaintenanceMode(maintenanceMode === 'true');
} catch (error) {
console.error('Failed to fetch maintenance mode status:', error);
}
};
// Initial check
checkMaintenanceMode();
// Set up an interval to recheck every 60 seconds
const intervalId = setInterval(checkMaintenanceMode, 60000);
// Clean up interval on component unmount
return () => clearInterval(intervalId);
}, []);
return isMaintenanceMode;
};
export default useMaintenanceMode;
Here’s what’s happening in the hook:
- Initial Check: When the app loads, it fetches the current page to read the
X-Maintenance-Mode
header. - 60-Second Interval: A
setInterval
ensures the app rechecks the status every 60 seconds. - Cleanup: The
clearInterval
function prevents memory leaks when the component using the hook is unmounted.
Integrating the Hook with React Router
Once the hook was ready, I integrated it into the app’s routing logic. The goal was to show a maintenance page whenever maintenance mode was active. Here’s how I used the hook in the app:
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import MaintenancePage from './MaintenancePage';
import HomePage from './HomePage';
import AboutPage from './AboutPage';
import useMaintenanceMode from './useMaintenanceMode';
const App: React.FC = () => {
const isMaintenanceMode = useMaintenanceMode();
return (
<Router>
{isMaintenanceMode ? (
<Switch>
{/* Render MaintenancePage for all routes */}
<Route path="*" component={MaintenancePage} />
</Switch>
) : (
<Switch>
{/* Define regular app routes */}
<Route path="/" exact component={HomePage} />
<Route path="/about" component={AboutPage} />
{/* Add additional routes as needed */}
</Switch>
)}
</Router>
);
};
export default App;
With this setup:
- If
isMaintenanceMode
istrue
, the app renders theMaintenancePage
for all routes. - When the header value changes to
false
, the regular routes automatically become accessible without requiring a page reload.
Designing the Maintenance Page
The maintenance page needs to be simple and informative. Here’s the MaintenancePage
component I used:
import React from 'react';
const MaintenancePage: React.FC = () => {
return (
<div style={{ textAlign: 'center', marginTop: '20vh' }}>
<h1>Under Maintenance</h1>
<p>We’re working hard to improve your experience. Please check back later!</p>
</div>
);
};
export default MaintenancePage;
This minimal design ensures users know what’s happening without clutter or unnecessary detail.
Testing the Setup
Before deploying, I tested the maintenance mode implementation:
- Local Testing: I used a mock server to simulate Nginx’s behavior. Tools like Postman helped verify that the app correctly read the
X-Maintenance-Mode
header. - Simulated Downtime: I switched the Nginx header between
"true"
and"false"
to confirm the app updated dynamically. - Performance Check: I verified that the periodic checks didn’t introduce noticeable delays or resource usage issues.
Deploying and Managing Maintenance Mode
After confirming the setup worked as expected, I deployed the updated app to production. Managing maintenance mode is now straightforward:
- Activate Maintenance Mode: Update the Nginx configuration to set
X-Maintenance-Mode
to"true"
and reload Nginx. - Deactivate Maintenance Mode: Change the header to
"false"
and reload.
Since the app checks for updates every 60 seconds, any change in the header takes effect almost immediately for active users.
Wrapping Up
Setting up a dynamic maintenance mode with Vite, React, and Nginx turned out to be both practical and efficient. By leveraging Nginx headers and a custom React hook, I built a solution that doesn’t require page reloads or redeploys to activate. This approach keeps users informed during downtime while minimizing disruption.
Maintenance mode may seem like a small feature, but it plays a big role in maintaining a polished user experience during updates. If you’re running a production app, this setup is definitely worth adding to your toolkit.