I’ve spent the better part of the last three years arguing with React Native’s layout engine. If you’ve been doing this as long as I have, you know the drill. You write a beautiful, responsive layout in Tamagui, it looks pristine on the web, and then you open the Android emulator. Disaster. Shadows look like 8-bit glitches, gradients are missing, and don’t even get me started on the absolute hack-job required to get a simple blur effect working.
That’s why I almost spilled my coffee this morning reading the 0.77 release notes.
We finally have proper web alignment. Not “close enough” alignment. Not “it works if you install three native dependencies” alignment. Actual, native support for properties we’ve been taking for granted in CSS for a decade.
The Death of the Shadow Hack
Let’s be real: elevation was a mistake. For years, if you wanted a shadow on Android, you had to use this arbitrary integer that looked different on every device, while iOS got the nice shadowColor, shadowOffset, and shadowOpacity trio. Trying to unify them in a cross-platform design system was a nightmare. You’d end up writing utility functions just to normalize how a simple card looked.
Tamagui handled this better than most, doing a lot of the heavy lifting at compile time, but it was still generating different native instructions under the hood. It felt fragile.
With 0.77, we can finally just use boxShadow. One property. Everywhere.
I threw together a quick test using the new Tamagui beta that supports this. Look how much cleaner the definition is. I didn’t have to think about platform-specific files or conditional logic.
import { Stack, Text, styled } from 'tamagui'
const ModernCard = styled(Stack, {
backgroundColor: '$background',
padding: '$4',
borderRadius: '$4',
// This used to be a mess of platform-specific styles
// Now it just works on iOS, Android, and Web
boxShadow: '0px 4px 12px rgba(0,0,0,0.1)',
hoverStyle: {
boxShadow: '0px 8px 24px rgba(0,0,0,0.15)',
y: -2,
},
pressStyle: {
boxShadow: '0px 2px 8px rgba(0,0,0,0.1)',
y: 0,
}
})
export function CardDemo() {
return (
Finally, consistent shadows.
)
}
It sounds small, but when you’re building a system with hundreds of components, removing that mental overhead is huge. I ran this on a Pixel 8 and an iPhone 15, and they looked identical. No third-party gradient libraries, no SVG hacks. It just rendered.
Why Tamagui Devs Should Care More Than Anyone
If you’re writing vanilla React Native, this update is nice. If you’re using Tamagui, it’s actually significant for performance.
Tamagui’s secret sauce has always been its compiler. It takes your high-level styles and flattens them into native primitives. Before 0.77, if you used a web-centric property, Tamagui often had to do some complex expansion or runtime polyfilling to make React Native understand it. That meant more JavaScript running on the main thread, or larger bundle sizes because of the extra logic needed to bridge the gap.
Now? The compiler can literally just pass the values through.
I was digging through the node_modules earlier (yeah, I’m that guy) to see how the latest Tamagui release handles this. The output is shockingly boring, which is exactly what you want. It maps boxShadow directly to the native view layer props. Less processing, faster render times.
The Filter Property is Actually Usable
The other bit that caught me off guard was the filter support. We’ve had blur() in various capacities before, usually via @react-native-community/blur or Expo’s blur view. But standard CSS filters? Nope.
RN 0.77 brings a subset of CSS filters directly into the style prop. This is massive for UI polish. I’m talking about things like grayscale for disabled states or brightness adjustments on press, handled natively on the GPU.
I tried adding a simple grayscale transition to an image avatar. Previously, I would have swapped the image source or used an overlay. Now:
const Avatar = styled(Image, {
width: 100,
height: 100,
borderRadius: 50,
variants: {
status: {
offline: {
filter: 'grayscale(100%)',
opacity: 0.7,
},
online: {
filter: 'grayscale(0%)',
opacity: 1,
}
}
}
})
It worked instantly. No native module linking errors, no build failures. It felt like writing for the web.
Is it perfect? No.
I don’t want to sound like a fanboy here. There are still annoying gaps. We still don’t have full Grid support (though Flexbox gap is finally behaving itself properly in nested views), and text handling remains… well, React Native text handling. If you’re expecting 100% CSS parity, you’re going to be disappointed.
But the direction is clear. The React Native team is effectively admitting that the Web API won. Instead of inventing their own proprietary ways of defining UI, they’re adopting the standards we all know.
For us Tamagui users, this validates the bet we made on the framework. Tamagui was built on the premise that you should write standard syntax and let the tools figure out the implementation. As React Native gets closer to the web, Tamagui doesn’t become obsolete—it becomes more efficient. It stops being a translator and starts being a pure optimizer.
I’m upgrading my current project to 0.77 this weekend. I’m probably going to break everything, because that’s how upgrades go, but for the first time in a while, the new features seem worth the pain.











