Skip what users can't see
Skip layout for off-screen rows; hide toggled UI instead of unmounting.
From vercel-react-best-practices: rendering-content-visibility and rendering-activity.
Lay out 1,000 messages
Press Render both, then scroll the right list.
Plain listBad
- Layout
- –
- First frame
- –
- Laid out
- –
.message-item { }content-visibilityGood
- Layout
- –
- First frame
- –
- Laid out
- –
content-visibility: auto; contain-intrinsic-size: 0 80px.message-item {
content-visibility: auto;
contain-intrinsic-size: 0 80px;
}
function MessageList({ messages }: { messages: Message[] }) {
return (
<div className="overflow-y-auto h-screen">
{messages.map(msg => (
<div key={msg.id} className="message-item">
<Avatar user={msg.author} />
<div>{msg.content}</div>
</div>
))}
</div>
)
}
No size hint, jumpy scrollbar
Scroll both lists and watch the total height.
No intrinsic sizeBad
- Total height
- –
- Height changes
- 0
content-visibility: autoWith a size estimateGood
- Total height
- –
- Height changes
- 0
contain-intrinsic-size: 0 80px.message-item {
content-visibility: auto;
contain-intrinsic-size: auto 80px;
}
An expensive menu
Open, filter and tick, then close and reopen.
Conditional renderBad
Not opened yet
{isOpen && <ExpensiveMenu />}ActivityGood
Not opened yet
<Activity mode={isOpen ? 'visible' : 'hidden'}>import { Activity } from 'react'
function Dropdown({ isOpen }: Props) {
return (
<Activity mode={isOpen ? 'visible' : 'hidden'}>
<ExpensiveMenu />
</Activity>
)
}
Why it matters, pros and cons
Why it matters
content-visibility skips off-screen layout; Activity hides UI without unmounting.
Pros
- Two CSS declarations, no virtualization library
- Find-in-page and anchors still reach rows
- Reopened UI keeps its state
Cons
- React still renders every row; virtualize at tens of thousands
- A poor size estimate makes the scrollbar jump
- Hidden trees hold memory; reset forms should unmount