Jank
Also called stutter or dropped frames
Visible stutter in an animation or scroll, caused when the browser misses frames because it cannot finish its work in time.
import { useState } from 'react';
export function ProjectSidebar({ projects }: { projects: string[] }) {
const [open, setOpen] = useState(false);
const [settled, setSettled] = useState(false);
const [query, setQuery] = useState('');
// Filter only once the panel has stopped moving.
const visible = settled
? projects.filter((name) =>
name.toLowerCase().includes(query.toLowerCase())
)
: [];
function toggle() {
if (!open) setSettled(false);
setOpen(!open);
}
return (
<>
<button
type='button'
aria-expanded={open}
onClick={toggle}
className='rounded-md border px-3 py-2 text-sm font-medium hover:bg-muted'
>
Projects
</button>
<aside
inert={!open}
// translate and opacity skip layout on every frame; width or left would not.
className={`fixed inset-y-0 left-0 flex w-72 flex-col gap-3 border-r bg-background p-4 transition-[translate,opacity] duration-250 ease-[cubic-bezier(0.23,1,0.32,1)] ${open ? '' : '-translate-x-full opacity-0'}`}
// Heavy work waits for the slide to finish, so it can't steal frames from it.
onTransitionEnd={(event) => {
if (event.propertyName === 'translate') setSettled(open);
}}
>
<input
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder='Filter projects'
className='rounded-md border bg-background px-3 py-2 text-sm'
/>
{settled ? (
<ul className='overflow-y-auto text-sm'>
{visible.map((name) => (
<li key={name} className='rounded-md px-3 py-2 hover:bg-muted'>
{name}
</li>
))}
</ul>
) : (
<p className='text-sm text-muted-foreground'>Loading projects…</p>
)}
</aside>
</>
);
}The sidebar stutters when it opens. Animate it with transform: translateX() and opacity instead of width or left, defer the list filtering until the 250ms open animation finishes, and confirm in the Chrome DevTools Performance panel that no frames drop during the animation.
Paste into Layerwise or any AI coding tool.
What is Jank?
Jank is the hitch you notice when motion freezes for a moment and then skips ahead. Each frame has a deadline, about 16.7ms on a 60Hz screen. When the browser misses it, the old frame stays on screen, and the next one jumps to catch up.
Common causes are long JavaScript tasks on the main thread, animating properties that trigger layout such as width, top, or left, expensive paints like large blurs and shadows, and decoding big images in the middle of an animation.
To find it, record the interaction in the Performance panel of Chrome DevTools: dropped frames show up in the Frames track, and long tasks get a red marker. Fix it by animating transform and opacity, breaking long tasks into smaller pieces, and moving heavy work out of the moment the animation runs.
Steady motion feels better than motion that alternates between fast and frozen, so a consistent frame rate matters as much as a high one.
Often confused with
- Frame rate (FPS)
- Frame rate is how many frames arrive each second on average. Jank is the uneven gaps, when some frames arrive late or not at all.
Related terms
All termsFrame rate (FPS)
The number of frames a screen draws each second; around 60 fps, motion looks smooth, and at lower rates it starts to look choppy.
Layout thrashing
A slowdown caused by code that alternates style changes with size reads, forcing the browser to recalculate layout many times in one frame.
GPU acceleration
A browser technique where the graphics chip moves, fades, or scales an element on its own layer instead of redrawing the page every frame.
Perceived performance
How fast an app feels to people, which skeleton screens, instant feedback, and progressive loading can improve even when the real wait stays the same.
Build it with Layerwise
Describe your app, watch it run in a live preview, and publish it with a database, storage, and sign-in built in.