DEV Community

Kashyap Savaliya
Kashyap Savaliya

Posted on

I Tried Building iOS 26’s Liquid Glass UI in React Native Here’s What Nobody Tells You

A React Native developer’s honest experience with Expo UI and SwiftUI, without leaving JavaScript

So last month, our designer dropped a Figma file on me and said, “Make it look like the new iOS Settings app. That glass wala effect.”

If you have seen iOS 26, you know what I am talking about Liquid Glass. That frosted, blurry, alive-looking material Apple put everywhere. Looks beautiful. And my first thought as a React Native dev was simple: bhai, this is going to be painful.

Because normally, this is how it goes for us. Apple ships some new visual thing → we wait 6 months for a community library → the library is 80% accurate → the designer notices the 20%.

But this time it was different. Expo shipped something called Expo UI, and honestly, it changed how I think about writing native-feeling apps in React Native. Let me explain what I learned, including the parts that confused me in the beginning.

First, forget what you think Expo UI is

When I heard “Expo UI”, I assumed it’s another component library. Like react-native-paper or NativeBase. Pre-made buttons, some theme system, done.

It is not that. At all.

Expo UI is basically a bridge to real SwiftUI. When you write from Expo UI, iOS is not rendering a JavaScript-styled fake button. It is rendering an actual SwiftUI button the same one Apple's own apps use. On Android, the plan is the same idea with Jetpack Compose (that part is still coming, more on that later).

One line summary I keep telling my colleagues:

Other libraries copy the native look. Expo UI is the native look.

Why does this matter? Because Apple has started shipping things that only exist in SwiftUI. Liquid Glass is one. There are components with no UIKit equivalent at all. If you are stuck in the old bridge world, you simply cannot access them. This is Apple slowly closing the door, and Expo UI is keeping our foot in it.

The thing that confused me for 20 minutes

The very first thing that will trip you up: every Expo UI tree must start with a component.

My initial reaction was “why extra wrapper, yaar?” But then it clicked. Think of it like this your React Native screen is one country, SwiftUI is another country. is the border checkpoint. Inside it, SwiftUI rules apply. Outside it, normal React Native rules apply.

import { Host, VStack, Text } from '@expo/ui/swift-ui';

export default function ProfileCard() {
return (
<Host matchContents>
<VStack spacing={8}>
<Text size={20}>Kashyap</Text>
<Text size={14}>React Native Developer</Text>
</VStack>
</Host>
);
}

The matchContents prop tells the Host to size itself to whatever SwiftUI renders inside. Without it, I was getting invisible components and thought the library was broken. It was not. I was.

Biggest gotcha: inside , your flexbox knowledge is useless. No flexDirection, no justifyContent. SwiftUI has its own layout world VStack for vertical, HStack for horizontal. Flexbox works on the Host from outside, but never inside it. Took me one full evening of console.logging nothing to figure this out.

The Liquid Glass part (why you clicked on this article)

Okay, the fun bit. In SwiftUI, styling happens through modifiers small functions you chain onto a view. Expo UI exposes these as an array. And yes, the glass one works.

Here I am putting Liquid Glass on a floating action button a very common real-world use case, because glass FABs over scrolling content is basically the signature iOS 26 look:

import { Host, Button, Image } from '@expo/ui/swift-ui';
import { glassEffect, frame } from '@expo/ui/swift-ui/modifiers';
function GlassFAB({ onPress }) {
return (
<Host style={{ position: 'absolute', bottom: 40, right: 24 }}>
<Button
onPress={onPress}
modifiers={[
frame({ width: 56, height: 56 }),
glassEffect({ glass: { variant: 'regular' } }),
]}
>
<Image systemName="plus" size={22} />
</Button>
</Host>
);
}

That’s it. No native module writing, no Swift file in your project, no pod nonsense. The blur, the light refraction, the way it reacts when content scrolls behind it all of that comes free because it is Apple’s implementation.

There is also a GlassEffectContainer for grouping multiple glass elements so they merge and morph into each other like liquid (the fancy demos you have seen on Twitter). And if you just want glass behind a normal React Native without entering SwiftUI world at all, there is a separate small package called expo-glass-effect for that. Two different tools for two different levels of commitment.

The component that sold me completely

Forget glass for a second. You know what actually made me a believer? Building a settings screen.

Every React Native dev has hand-built an iOS-style settings screen at least once. The grey background, the white rounded cards, the hairline separators at exactly the right left margin, the 44pt touch targets. It’s easily 100+ lines of StyleSheet, and it still looks slightly off on every iOS update.

With SwiftUI’s Form, Apple handles all of that. Expo UI gives it to you directly:

import { Host, Form, Section, Switch, Picker, Button } from '@expo/ui/swift-ui';
function SettingsScreen() {
const [notifs, setNotifs] = useState(true);
const [quality, setQuality] = useState(1);
return (
<Host style={{ flex: 1 }}>
<Form>
<Section title="Playback">
<Switch label="Notifications" value={notifs} onValueChange={setNotifs} />
<Picker
label="Video Quality"
options={['Auto', '720p', '1080p']}
selectedIndex={quality}
onOptionSelected={({ nativeEvent }) => setQuality(nativeEvent.index)}
/>
</Section>
<Section title="Account">
<Button role="destructive" onPress={logout}>Log Out</Button>
</Section>
</Form>
</Host>
);
}

Maybe 25 lines. Zero StyleSheet. And it doesn’t just look native it behaves native. Dark mode, dynamic type for accessibility, future iOS redesigns all automatic. When iOS 27 comes and Apple changes the Settings look again, my screen updates itself. That is the real win nobody talks about.

Honest gotchas before you jump in

I don’t want to write one of those “everything is amazing” posts. Here is what bit me:

  1. It’s iOS-first right now. Jetpack Compose support for Android is on the roadmap but not there yet. So @expo/ui/swift-ui code needs a Platform.OS check or a .ios.tsx file, with a fallback for Android. Plan your component structure accordingly.

  2. APIs are still moving. This is an evolving library. Things got restructured between SDK 53 and SDK 54 (the whole pattern itself is newer). Pin your versions and read the changelog before upgrading. Don't build your entire app's foundation on it yet decorate with it instead.

  3. Mixing has rules. You can put normal React Native components inside SwiftUI components as children Expo UI wraps them automatically. But layout behaviour gets weird if you fight it, because SwiftUI takes full control of positioning for wrapped views. My advice from experience: keep the boundary clean. Either a section of screen is SwiftUI world or it is RN world. Don’t sandwich them five levels deep.

  4. You need SDK 54+ and iOS 26 for glass. The glassEffect modifier obviously needs a device/simulator running iOS 26. On older iOS it degrades, so test your fallback.

So should you use it?

My honest take after shipping with it:

  • Building iOS-heavy features where native feel matters (settings, forms, system-style sheets)? Yes, immediately. The Form example alone will save you days.
  • Want that Liquid Glass premium look without touching Xcode? Yes. This is currently the easiest path in the RN ecosystem, full stop.
  • Need pixel-identical UI across iOS + Android today? Stick to regular React Native views for now, and sprinkle Expo UI only on iOS where it counts.

The bigger picture is what excites me. For years, React Native’s story was “write once, look same everywhere.” Expo UI is quietly adding a second story: “write in React, but let each platform look like itself.” Both are valid. Now we finally get to choose per component, not per app.

And as someone who has spent too many evenings faking iOS designs with borderRadius and shadowOpacity being able to just import the real thing feels like cheating. Good cheating.

If you try this out and hit weird layout issues, 90% chance it’s the flexbox-inside-Host mistake. thanks me later.

Follow me for more React Native + native iOS experiments. Claps appreciated if this saved you an evening 👏

And if you get stuck anywhere while trying this out, feel free to reach out - happy to help. You can find me at kashyapsavaliya.com

Top comments (0)