I’ve often hit a wall with conditional rendering. The standard {show && <Component />} is great, but it has one major flaw: it kills your state.
When you unmount a component, every piece of data in its useState or useRef hooks vanishes. This is a nightmare for things like multi-step tax forms or complex data dashboards.
I’ve spent countless hours debugging “lost data” bugs caused by unmounting. In this tutorial, I’ll show you exactly how to keep your components alive while keeping them hidden.
The Problem with Traditional Conditional Rendering
Most developers learn the && operator or ternary expressions early on. It’s the “standard” way to toggle UI elements in React.
While it keeps the DOM lean, it destroys the component instance. If your user is halfway through a form and you hide it, that progress is gone forever.
I prefer keeping the component in the DOM when the state needs to persist. This ensures that when the user comes back, they find everything exactly where they left it.
Method 1: Use CSS “display: none”
This is the most reliable method I’ve used across dozens of production apps. By toggling a CSS property, the component stays in the DOM, so React never clears its state.
I find this works best for sidebars or tabs where you want an instant “return” experience.
Below is a full example of a US Real Estate Calculator where we hide the results without losing the input data.
import React, { useState } from 'react';
// A sub-component with its own internal state
const MortgageResults = ({ isVisible }) => {
const [interestRate, setInterestRate] = useState(6.5);
return (
<div style={{ display: isVisible ? 'block' : 'none', padding: '20px', border: '1px solid #ccc' }}>
<h3>Mortgage Estimates (New York Market)</h3>
<p>Current Interest Rate: <strong>{interestRate}%</strong></p>
<button onClick={() => setInterestRate(prev => (parseFloat(prev) + 0.1).toFixed(1))}>
Adjust Rate (+0.1%)
</button>
<div style={{ marginTop: '10px' }}>
<p>Estimated Monthly Payment: $3,250</p>
<small>*Rates based on current NY averages.</small>
</div>
</div>
);
};
const PropertyDashboard = () => {
const [showResults, setShowResults] = useState(true);
return (
<div style={{ fontFamily: 'Arial, sans-serif', maxWidth: '600px', margin: 'auto' }}>
<h1>US Property Analysis Tool</h1>
<p>Toggle the results below. Notice that the Interest Rate doesn't reset!</p>
<button
onClick={() => setShowResults(!showResults)}
style={{ marginBottom: '20px', padding: '10px 20px', cursor: 'pointer' }}
>
{showResults ? 'Hide Results' : 'Show Results'}
</button>
{/* We render the component but control visibility via CSS.
This prevents unmounting and preserves the 'interestRate' state.
*/}
<MortgageResults isVisible={showResults} />
</div>
);
};
export default PropertyDashboard;You can refer to the screenshot below to see the output.

It’s simple, effective, and doesn’t require any complex state management libraries.
Method 2: The React 19 “Activity” Component
If you are working with the latest versions of React, there is a new way to handle this. React 19 introduced the <Activity> component (formerly known as Offscreen).
It’s essentially a performance-optimized version of Method 1.
Here is how I implement it for a California DMV Appointment Tracker.
import React, { useState, Activity } from 'react';
const AppointmentForm = () => {
const [zipCode, setZipCode] = useState('');
const [office, setOffice] = useState('Los Angeles - Hope St');
return (
<div style={{ padding: '20px', backgroundColor: '#f9f9f9', borderRadius: '8px' }}>
<h2>CA DMV Appointment Form</h2>
<label>Enter ZIP Code: </label>
<input
value={zipCode}
onChange={(e) => setZipCode(e.target.value)}
placeholder="90001"
/>
<br /><br />
<label>Preferred Office: </label>
<select value={office} onChange={(e) => setOffice(e.target.value)}>
<option value="Los Angeles - Hope St">Los Angeles - Hope St</option>
<option value="San Francisco - Fell St">San Francisco - Fell St</option>
<option value="Sacramento - 24th St">Sacramento - 24th St</option>
</select>
<p>Current Selection: {office} ({zipCode})</p>
</div>
);
};
const DmvPortal = () => {
const [viewMode, setViewMode] = useState('visible');
return (
<div style={{ padding: '40px' }}>
<h1>Official California DMV Portal</h1>
<button
onClick={() => setViewMode(viewMode === 'visible' ? 'hidden' : 'visible')}
style={{ marginBottom: '20px' }}
>
{viewMode === 'visible' ? 'Minimize Form' : 'Restore Form'}
</button>
{/* React 19 Activity Component:
Hides the children without losing state or running effects.
*/}
<Activity mode={viewMode}>
<AppointmentForm />
</Activity>
</div>
);
};
export default DmvPortal;You can refer to the screenshot below to see the output.

I love this method because it’s “native” to React’s engine. It hides the UI using display: none but also de-prioritizes background updates to save CPU.
Method 3: Use “visibility: hidden” or “opacity: 0”
Sometimes you want the component to stay in the DOM but also keep its physical space.
Using display: none removes the element from the document flow, making other items jump up.
I use visibility: hidden when I want to avoid layout shifts.
This is perfect for US Stock Market Tickers where the layout needs to remain stable.
import React, { useState } from 'react';
const StockTicker = ({ isVisible }) => {
const [lastUpdate] = useState(new Date().toLocaleTimeString());
return (
<div style={{
visibility: isVisible ? 'visible' : 'hidden',
height: '100px',
background: '#eef',
padding: '10px'
}}>
<h3>NASDAQ: AAPL</h3>
<p>Current Price: $185.92</p>
<small>Last synced at: {lastUpdate}</small>
</div>
);
};
const MarketWatch = () => {
const [showTicker, setShowTicker] = useState(true);
return (
<div style={{ padding: '30px' }}>
<h1>Wall Street Watchlist</h1>
<p>Hiding the ticker below will NOT move the text under it.</p>
<button onClick={() => setShowTicker(!showTicker)}>
Toggle Visibility
</button>
<hr />
<StockTicker isVisible={showTicker} />
<div style={{ marginTop: '20px', color: '#666' }}>
<p>This paragraph stays in place because the ticker still occupies its space.</p>
</div>
</div>
);
};
export default MarketWatch;You can refer to the screenshot below to see the output.

The element becomes invisible, but it still occupies its width and height.
When Should You Still Use Unmounting?
Even though I’ve shown you how to avoid unmounting, it’s not always the enemy.
If a component is extremely “heavy”, like a large 3D map or a table with 10,000 rows—keep it unmounted.
Hiding components with CSS keeps them in the DOM and the Virtual DOM. This means React still has to track them, which can slow down your app if overused.
I generally follow a simple rule of thumb. If the state is valuable to the user, hide it. If the state is useless, unmount it.
Best Practices for Hidden Components
When I keep components hidden, I always make sure to handle side effects.
For instance, if your hidden component has a setInterval, it will keep running in the background.
Always check the visibility prop inside your useEffect hooks. This prevents unnecessary API calls or battery drain for your users.
I also recommend using ARIA attributes like aria-hidden=”true”. This ensures that screen readers don’t try to read the “invisible” content to the user.
In this tutorial, I covered three different ways to hide a React component without unmounting. Each method has its own use case depending on whether you want to preserve the layout or optimize for background performance.
I find that using the CSS display: none approach is usually the best for most standard web applications, while the new React 19 Activity component is perfect for high-performance apps that need background state management.
You may read:
- Ways to Display JSON in React
- How to Access React Context Outside of a Component
- How to Use Shadcn UI in React
- How to Pass Arguments to React Components

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.