Skip to content

Instantly share code, notes, and snippets.

@mlaopane
Created November 13, 2025 20:57
Show Gist options
  • Select an option

  • Save mlaopane/61d805b7dba3be3512800bf6f8d855e0 to your computer and use it in GitHub Desktop.

Select an option

Save mlaopane/61d805b7dba3be3512800bf6f8d855e0 to your computer and use it in GitHub Desktop.
Mantine Modal with scrollable body
import { Modal, Box, Flex, Text, Paper, ScrollArea } from "@mantine/core";
import { PropsWithChildren } from "react";
interface Props {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
}
export const CustomModal = ({ isOpen, onClose, children }: Props) => {
return (
<Modal
opened={isOpen}
onClose={onClose}
withCloseButton
size="xl"
styles={{
root: {
"--modal-size-xl": "960px",
},
body: {
padding: 0, // remove default padding so our layout is clean
},
header: {
height: 12,
position: "absolute",
padding: 0,
right: "1rem",
},
}}
>
{/* Container that fills the modal */}
<Flex
style={{
flexDirection: "column",
height: "70vh",
minHeight: "300px",
overflow: "hidden",
}}
>
{children}
</Flex>
</Modal>
);
};
const Body = ({ children }: PropsWithChildren) => {
return (
<Box
style={{
flex: 1,
overflowY: "auto",
padding: "1rem",
}}
>
<Paper bg="lightgrey" h="100%" mih={0}>
<ScrollArea h="100%" p={16} scrollbarSize={8}>
{children}
</ScrollArea>
</Paper>
</Box>
);
};
const Header = ({ children }: PropsWithChildren) => {
return (
<Flex
style={{
padding: "1rem",
borderBottom: "1px solid var(--mantine-color-gray-3)",
flexShrink: 0,
}}
>
{children}
</Flex>
);
};
const Footer = ({ children }: PropsWithChildren) => {
return (
<Flex
style={{
padding: "1rem",
borderTop: "1px solid var(--mantine-color-gray-3)",
flexShrink: 0,
}}
>
{children}
</Flex>
);
};
CustomModal.Body = Body;
CustomModal.Header = Header;
CustomModal.Footer = Footer;
import { MantineProvider, Text } from "@mantine/core";
import { useMemo, useState } from "react";
import { CustomModal } from "./CustomModal";
import "./styles.css";
import "@mantine/core/styles.css";
export const Parent = () => {
const modalManager = useModalManager();
return (
<MantineProvider>
<div>
<h2>Modal gist</h2>
<div>
<button onClick={modalManager.open}>Open the modal</button>
</div>
<CustomModal isOpen={modalManager.isOpen} onClose={modalManager.close}>
<CustomModal.Header>
<Text fz={18} fw={700} component="span">
My Modal Header
</Text>
</CustomModal.Header>
<CustomModal.Body>
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
Lorem ipsum Lorem ipsum Lorem ipsum <br />
</CustomModal.Body>
<CustomModal.Footer>
<button onClick={modalManager.close}>Close</button>
</CustomModal.Footer>
</CustomModal>
</div>
</MantineProvider>
);
}
function useModalManager(isInitiallyOpen = false) {
const [isOpen, setIsOpen] = useState(isInitiallyOpen);
const open = () => {
setIsOpen(true);
};
const close = () => {
setIsOpen(false);
};
return useMemo(() => ({ isOpen, open, close }), [isOpen, open, close]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment