Today, I was experimenting with shadcnUI’s drawer to build a simple map-like interface. I quickly got it to work with two snap points. The first point would show a title. The second point the entire content. It worked great.
However, when I tested it on my phone, I realized the points were off. On safari, the first point would show the title and 2 lines of conent. On Chrome it would show 4 lines of content. On a different phone it didn’t even show the title.
Finally, I realized it was the difference in window heights, and how Vaul, the library behind the drawer, calculates the offset of each snap point. Here’s how I solved it:
Forcing consistency in snap points
To get deterministic snapping points, make your drawer’s height 100% of the window height. If you’re using Tailwind, that would look like h-full
.
This will preserve pixel accuracy, ensuring your content always snaps at the same element. Independent of device height.
Then, set the maximum snapPoint
to a value less than 100% to make it look like with dynamic height (h-auto
).
While the PR is finished and merged, make your DrawerContent
height equal to the window height. This will ensure your default state transform3d(0,0,0)
is aligned with the window height. Ensuring consistent transformations.
To compensate for the default state now being 100% of the screen, you can set a maximum snap point with the size of your content. Here’s what the full component would look like:
import {useState} from "react"
import {Drawer} from "@/components/ui/drawer"
func DrawerWithSnapPoints(){
const snapPoints = [100px, 500px]
cons [snapPoint, setSnapPoint] = useState(snapPoints[1])
return (
<Drawer
>
...
<DrawerContent className="w-full>
</DrawerContent>
</Drawer>
)
}
Some people are working on implementing a Drawer.SnapPoint
component that would enable defining snap points based on content.
Why snap points are inconsistent in the default implementation
Vaul moves the drawer up and down via the translate3d
CSS function. This enables a smooth transition between states via the cubic-bezier
function.
The default state of an open drawer is fully open. At this point, the translate function doesn’t perform any transformations, it is set to translate3d (0,0,0)
.
Then, for each snap point, Vaul calculates how much it should move the top of the drawer downwards to get the expected height. For instance, if the window’s height is 1000px, a snap point of 200px would require a downward translation of 800px.
[INSERT IMAGE]
If the drawer’s height is different to the window height, the drawer’s snap points won’t be consistent.
[INSERT IMAGE].