Skip to content

Instantly share code, notes, and snippets.

@SuperZombi
Last active April 12, 2026 23:10
Show Gist options
  • Select an option

  • Save SuperZombi/de74220af33636eea8294824e044fea6 to your computer and use it in GitHub Desktop.

Select an option

Save SuperZombi/de74220af33636eea8294824e044fea6 to your computer and use it in GitHub Desktop.
Universal React Popup
const App = () => {
	const onClose = _=>{ console.log("closed") }
	return (
		<Popup onClose={onClose}>
			<h3>Hello world!</h3>
		</Popup>
	)
}

Popup Component

const Popup = ({onClose, children}) => {
	const [show, setShow] = React.useState(false)
	React.useEffect(_=>{
		setTimeout(_=>setShow(true), 0)
	}, [])
	const BeforeClose = _=>{
		if (show){
			setShow(false)
			setTimeout(_=>{ onClose() }, 350)
		}
	}
	return (
		<div className={`popup ${show && "show"}`}
			onClick={e=>e.target.classList.contains("popup") && BeforeClose()}
		>
			<div className="popup-content">
				<span className="close" onClick={_=>BeforeClose()}>X</span>
				{children}
			</div>
		</div>
	)
}

Styles:

.popup {
	position: fixed;
	inset: 0;
	z-index: 10;
	display: flex;
	align-items: center;
	justify-content: center;
	visibility: hidden;
	transition: 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.popup.show {
	visibility: visible;
	background: rgb(0, 0, 0, 0.5);
	backdrop-filter: blur(5px);
}

.popup .popup-content {
	position: relative;
	background: white;
	scale: 0.95;
	opacity: 0;
	transform: translateY(1rem);
	will-change: scale, opacity, transform;
	transition: 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.popup.show .popup-content {
	transform: translateY(0);
	opacity: 1;
	scale: 1;
}

.popup .close {
	position: absolute;
	top: 1rem;
	right: 1rem;
	transition: 0.2s;
	will-change: rotate;
}
.popup .close:hover {
	rotate: 90deg;
}

Add custom close button

const App = () => {
	const onClose = _=>{ console.log("closed") }
	return (
		<Popup onClose={onClose} closeButton={false} closeOnOutsideClick={false}>
			{({ close }) => (
				<>
					<h3>Hello world!</h3>
					<button onClick={close}>OK</button>
				</>
			)}
		</Popup>
	)
}

Component

const Popup = ({
	onClose, children,
	closeButton=true,
	closeOnOutsideClick=true
}) => {
	const [show, setShow] = React.useState(false)
	React.useEffect(_=>{
		setTimeout(_=>setShow(true), 0)
	}, [])
	const BeforeClose = _=>{
		if (show){
			setShow(false)
			setTimeout(_=>{ onClose && onClose() }, 350)
		}
	}
	return (
		<div className={`popup ${show && "show"}`}
			onClick={closeOnOutsideClick && (
				e=>e.target.classList.contains("popup") && BeforeClose()
			)}
		>
			<div className="popup-content">
				{closeButton && (
					<span className="close" onClick={_=>BeforeClose()}>X</span>
				)}
				{typeof children === "function" ? children({ close: BeforeClose }) : children}
			</div>
		</div>
	)
}

Try it yourself:

<!DOCTYPE html>
<html>
<head>
<title>React Popup</title>
<link rel='stylesheet' href='https://use.fontawesome.com/releases/v6.7.2/css/all.css'>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
body {
font-family: sans-serif;
text-align: center;
}
button {
cursor: pointer;
}
.popup {
position: fixed;
inset: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
visibility: hidden;
transition: 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.popup.show {
visibility: visible;
background: rgb(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.popup .popup-content {
position: relative;
background: white;
padding: 1rem;
border-radius: 1rem;
scale: 0.95;
opacity: 0;
transform: translateY(1rem);
will-change: scale, opacity, transform;
transition: 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.popup.show .popup-content {
transform: translateY(0);
opacity: 1;
scale: 1;
}
.popup .close {
color: red;
cursor: pointer;
position: absolute;
top: 0.5rem;
right: 0.5rem;
transition: 0.2s;
will-change: rotate;
}
.popup .close:hover {
rotate: 90deg;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const Popup = ({
onClose, children,
closeButton=true,
closeOnOutsideClick=true
}) => {
const [show, setShow] = React.useState(false)
React.useEffect(_=>{
setTimeout(_=>setShow(true), 0)
}, [])
const BeforeClose = _=>{
if (show){
setShow(false)
setTimeout(_=>{ onClose && onClose() }, 350)
}
}
return (
<div className={`popup ${show && "show"}`}
onClick={closeOnOutsideClick && (
e=>e.target.classList.contains("popup") && BeforeClose()
)}
>
<div className="popup-content">
{closeButton && (
<i className="close fa-solid fa-circle-xmark" onClick={_=>BeforeClose()}></i>
)}
{typeof children === "function" ? children({ close: BeforeClose }) : children}
</div>
</div>
)
}
const App = () => {
const [renderPopup, setRenderPopup] = React.useState(false)
const onClose = _=>{
console.log("closed")
setRenderPopup(false)
}
return (
<>
<button onClick={_=>setRenderPopup(true)}>Show popup</button>
{renderPopup && (
<Popup onClose={onClose}>
{({ close }) => (
<>
<h3>Hello world!</h3>
<button onClick={close}>OK</button>
</>
)}
</Popup>
)}
</>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<App/>)
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment