I am attempting to make my table header sticky while I scroll down the page. However, the stickyHeader prop only works if I set the parent element’s overflow property to initial. This isn’t a great solution since if I resize the window the data start overflowing off the table. Is there any other way to approach this?
Example:
Data flowing off the table
Here is a minimal reproducible version of how I constructed my table (sandbox link below):
function StatTable() {
return (
<TableContainer sx={{ borderRadius: 2, overflow: "initial" }}>
<Table stickyHeader>
<TableHead sx={{ position: "sticky", top: 64 }}>
<PositionBar pos={pos} setPos={setPos} />
<StyledTableRow>
<TableCell> {'name'} </TableCell>
<TableCell align="right">{'calories'}</TableCell>
<TableCell align="right">{'fat'}</TableCell>
<TableCell align="right">{'carbs'}</TableCell>
<TableCell align="right">{'protein'}</TableCell>
<TableCell align="right">{'type'}</TableCell>
</StyledTableRow>
</TableHead>
<TableBody>
{sortedRows.map((row) => {
return (
<StyledTableRow key={row.id}>
<TableCell> {row.name} </TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
<TableCell align="right">{row.type}</TableCell>
</StyledTableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
);
}
function createData(id, name, calories, fat, carbs, protein, type) {
return {
id,
name,
calories,
fat,
carbs,
protein,
type,
};
}
const rows = [
createData(1, "Cupcake", 305, 3.7, 67, 4.3, 1),
createData(2, "Donut", 452, 25.0, 51, 4.9, 2),
createData(3, "Eclair", 262, 16.0, 24, 6.0, 3),
createData(4, "Frozen yoghurt", 159, 6.0, 24, 4.0, 3),
createData(5, "Gingerbread", 356, 16.0, 49, 3.9, 2),
createData(6, "Honeycomb", 408, 3.2, 87, 6.5, 1),
createData(7, "Ice cream sandwich", 237, 9.0, 37, 4.3, 2),
createData(8, "Jelly Bean", 375, 0.0, 94, 0.0, 3),
createData(9, "KitKat", 518, 26.0, 65, 7.0, 2),
createData(10, "Lollipop", 392, 0.2, 98, 0.0, 1),
createData(11, "Marshmallow", 318, 0, 81, 2.0, 3),
createData(12, "Nougat", 360, 19.0, 9, 37.0, 2),
createData(13, "Oreo", 437, 18.0, 63, 4.0, 1),
];
Toolbar for the table:
function PositionBar(props) {
return (
<Toolbar
variant="dense"
disableGutters
sx={{ minHeight: 20, height: 20, maxWidth: "100%" }}
>
{[1, 2, 3].map((pos) => {
return (
<Button key={pos} size="small">
{pos}
</Button>
);
})}
</Toolbar>
);
}
AppBar/NavBar:
function NavBar() {
return (
<AppBar position="sticky">
<Toolbar>
<Typography variant="h6" sx={{ mr: 2 }}>
Website
</Typography>
<Button variant="contained" disableElevation sx={{ mr: 2 }}>
Tab A
</Button>
<Button variant="contained" disableElevation sx={{ mr: 2 }}>
Tab B
</Button>
</Toolbar>
</AppBar>
);
}
And the whole app:
function App() {
return (
<div className="App">
<NavBar />
<Box
display="flex"
justifyContent="center"
alignItems="center"
minHeight="100vh"
>
<Paper elevation={3} sx={{ width: 0.8, p: 2 }}>
<StatTable />
</Paper>
</Box>
</div>
);
}
export default App;
Here is a more complete sandbox of my code:
Sandbox
Bonus: how do make the toolbar span the whole table so that data flows under it? Currently you can see the table body scroll past the header.
Example:
Table body ‘leaks’ through header
I have tried using stickyHeader without setting overflow but it just does not work. For the toolbar I have tried setting maxWidth to 100%, using containers, etc.