Loading video...

Video Failed to Load

Go Home

Build uncompromising high-performance, high-fidelity apps with React. What you see is not the Apple Messages app, nor is it meant to be 1:1. Rather it serves as an aspirational and real example of UI/UX that is possible. Every detail from the bubble animations to keyboard gestures to scroll view...

131,262 views • 2 days ago •via X (Twitter)

40 Comments

James Ide's profile picture
James Ide2 days ago

Running on Android. I don’t think this is what people expected when they asked for Android to get blue bubbles :P

Gregory Moskaliuk's profile picture
Gregory Moskaliuk2 days ago

Wow, this looks insane! If you ever need rich formatting inside those custom bubbles without tanking performance, we built react-native-enriched-markdown for this exact use case:

lauren's profile picture
lauren2 days ago

beautiful

James Ide's profile picture
James Ide2 days ago

Thank you!

Xuan Huang · 黄玄's profile picture
Xuan Huang · 黄玄2 days ago

@reactjs Beautiful UI Beautiful Writing Beautifully React Beautifully Native

Jamie Birch's profile picture
Jamie Birch2 days ago

The thing I am actually most astonished by is James’s laser-like accuracy when typing on the virtual keyboard during a recorded demo

Christoph Nakazawa's profile picture
Christoph Nakazawa2 days ago

Is this legal

James Ide's profile picture
James Ide2 days ago

Oh dear

Samuel Scheit's profile picture
Samuel Scheit1 day ago

Incredible work! Just a little nit-pick :) The send animation which transforms the input box into the message bubble needs a bit of polishing 1. bubble needs to stay visible at all times 2. bubble needs to stay on top of the input box 3. input box needs to slowly shrink in height

parbez's profile picture
parbez2 days ago

can you link to the repo? I want to steal some optimisations 😂

James Ide's profile picture
James Ide2 days ago

There’s a lot of code, what part do you want to know more about?

James Ide's profile picture
James Ide2 days ago

Animations: sending a chat bubble and revealing the timestamps on the side use Animated with the native driver. Reanimated would work well too. The Delivered/Read statuses and animating the bubbles’ positions use CSS Transitions. But without that API you could use (Re)Animated. Incoming chat messages and the typing indicator (not shown) use CSS Animations. The peek and pop effect is UIKit, including the way chat bubbles are lifted out of the scroll view and animate back to their original position when done. List: powered by VirtualView: There is some additional logic to make the chat bubbles cheap, like React.memo() and initializing the animated values only if a chat bubble is being sent and needs animation. The new scroll view I mentioned is for precise keyboard and safe area/inset handling and is not for speed. There is also some code to get the softly blurred headers on iOS 27. Native components: the chat bubbles are native because of the tails and animations. You could do this with SVG but it is likely faster to use CALayerPaths and there are potentially a lot of chat bubbles. The other native components are related to the keyboard and text composer because they are intrinsically coupled to UIKit’s delegate methods and must be frame-perfect. These pieces are infrastructure and I could see them maturing over time and being usable in most apps, instead of each developer rewriting their own implementation.

parbez's profile picture
parbez1 day ago

Thanks for the explanation

Spencer Taylor's profile picture
Spencer Taylor1 day ago

@expo Well said, and completely agree. “React and Native, not React vs. Native” Put that on a billboard.

Charles Vinette's profile picture
Charles Vinette1 day ago

Great post. An application is only as great as the level of polish and attention you give it.

Internet Labs's profile picture
Internet Labs1 day ago

Proving you can lock in 120 FPS by blending custom Swift layers with React Native completely buries the old bridge performance myth. Are you planning to package up that custom VirtualView scroll strategy as an open-source library?

James Ide's profile picture
James Ide1 day ago

VirtualView is open source and implements the cell rendering behavior: import {unstable_VirtualView as VirtualView} from 'react-native'; Docs: Stress test it with a few thousand views and work on making your cells as lightweight as you can. You’ll find it prioritizes rendering blanks (I believe this is also SwiftUI’s strategy) when the app can’t keep up. For real-world scrolling, it does a nice job of keeping scroll momentum and balancing how much off-screen content to pre-render.

Selva's profile picture
Selva1 day ago

Is the data static? Try fetching it from the backend using an infinite query, like you would in a real-world application. Or better yet, store it in expo-sqlite and read from there, with a background sync to keep it up to date. Will you still get 120 FPS?

James Ide's profile picture
James Ide1 day ago

The data is static which acts as a stress test for UI rendering since there is no network or data store to act as a bottleneck. The UI would have an easier time maintaining its frame rate when waiting for data with non-blocking fetches. I do think SQLite can perform very well, especially with the massive speed improvements in Expo Modules 2 and tuning how messages are fetched in batches. Would be good to measure it, I was focused just on the pure UI parts.

Selva's profile picture
Selva1 day ago

Well said! The UI work is beautiful.

Ahmad's profile picture
Ahmad2 days ago

does voiceover still read the bubbles in order with the timestamps moving separately??

Pavlo Nekrasov's profile picture
Pavlo Nekrasov2 days ago

Expo always delivers

Colin Hunt's profile picture
Colin Hunt1 day ago

Serious wow factor

Brian Hadu's profile picture
Brian Hadu1 day ago

aspirational examples are great, but they can set unrealistic expectations for developers

COURT's profile picture
COURT1 day ago

Can you please make an actual dummy build of this kit so we can play around with it and experience it before we signup? Just a mock iOS app in the AppStore that users can download and see the view in action. I don’t want to commit to anything without know if it will work for my project. It looks really great!

Serhii Y.'s profile picture
Serhii Y.1 day ago

"Uncompromised performance" is less impressive when it's only demonstrated on flagship hardware.

Nitesh ₿⚡️'s profile picture
Nitesh ₿⚡️1 day ago

Wish apple cared about the messages app as much as you did.

Tomas Martykan's profile picture
Tomas Martykan1 day ago

Very nice, GitHub link?

Azhar's profile picture
Azhar1 day ago

“Cofounder at expo” ah I see

Salem Shamakh 🇵🇸's profile picture
Salem Shamakh 🇵🇸1 day ago

Why you do not use legend list, also can you share code so we can learn from it

James Ide's profile picture
James Ide1 day ago

I wanted to try VirtualView. Legend List is great too I’m sure. It would be good to ship a lot of the infrastructural code into react-native core and Expo core upstream.

Hari · animata.design's profile picture
Hari · animata.design1 day ago

Great! Only that's bothering me is that sent message bubble animation

Gregor's profile picture
Gregor2 days ago

imo 'uncompromising' always loses to the reconciler the moment bubble animations hit real message volume

Stephen Rayner's profile picture
Stephen Rayner1 day ago

GitHub repo?

Forti's profile picture
Forti1 day ago

Make it open source 🥹

Juan Arreguin's profile picture
Juan Arreguin1 day ago

Those interaction details are my favorite part of building @PanolsApp too. Especially making the controls feel good while keeping the photos front and center.

Farzad Yousefzadeh's profile picture
Farzad Yousefzadeh1 day ago

@jamonholmgren good example demo for the recent debates you’ve been in? This can help widen the perspective of some blindly obsessed people you talk to 😊

Benji T's profile picture
Benji T1 day ago

React lookin' like Apple Messages and it ain't even tryin' to cosplay. This is the bar for mobile UI now.

Kgaugelo Kg's profile picture
Kgaugelo Kg2 days ago

What are the specific tradeoffs of the new scroll strategy during graceful degradation? Knowing these limitations would help determine when to swap it out for other options.

James Ide's profile picture
James Ide2 days ago

VirtualView skips rendering in favor of keeping scroll momentum. It will display blanks when it predicts the app will not meet its frame deadline, and the scroll view will keep moving with minimal stutter. In contrast, UIKit’s UITableView will freeze the scroll position until it has enough cells to display. This means there is always content on the screen but the scroll view is more likely to stutter when the app can’t keep up. Both approaches are valid and could share optimization strategies. For instance, in either case you could render a very cheap placeholder if you expect to miss your frame deadline. It would make VirtualView look better and UITableView perform better. And for the user, they’re often scrolling so fast in this scenario, it could be a good tradeoff to make.

Related Videos

AG-UI makes building agentic applications dramatically easier. Here's how it works. This is a model for a simple chatbot: User → LLM → Response But interactive agents that render UI, pause for approvals, and ask users for input need a much more complex model. When building these agents, a response from the LLM will include a series of state changes as the agent runs: • Agent started a task • Agent called a tool • Agent updated its state • Agent streams these tokens • Agent is waiting on a human • Agent is resuming the task The Agent-User Interaction Protocol (AG-UI) treats the LLM response as a stream of events rather than a text endpoint. In practice, here is what you get as an agent runs: 1. Lifecycle events so your UI knows where the agent is. 2. Text messages that stream tokens. 3. Tool calls so your UI can prefill a form with any required arguments. 4. State updates that keep your UI in sync with the agent. 5. Special events for human approvals, rich media, and custom needs. All of these events travel over standard transports (SSE, WebSockets, or plain HTTP) as JSON. As a result, you can build a frontend that stays in sync with the agent's progress without having to invent a custom process to make this happen. For example, building a human-in-the-loop workflow becomes an off-the-shelf component you can integrate rather than build from scratch. CopilotKit🪁 is the creator of AG-UI, and you can use it when building frontend applications pretty much anywhere: • React • Angular • Vue • React Native • Slack • Teams • Discord • WhatsApp • Telegram Here is the link for you to check it out: Thanks to the CopilotKit team for partnering with me on this post.

Santiago

17,438 views • 2 months ago

As many of you know, for the last five months I've been working full-time on my next big thing. The challenge was to invent something new and implement it entirely using LLMs for writing code. The first stage of the project is now complete: the web application, which I called is now online and accepting users. You can see a short demo in the video. 100% of the code of the app was generated by LLMs (mostly Gemini and Claude, maybe 10% of ChatGPT). I haven't written a single line of code. The tech stack is TypeScript, React, and Supabase/Postgres which was (and still is) fully new to me. During these five months, I implemented from scratch three versions of the software. It started as a Markdown editor to help me with my book writing and ended up as an AI-assisted reading and self-learning platform. What makes ChapterPal unique is a novel reading experience where the user can use the keyboard keys to reveal or "unreveal" the content and ask questions at any moment. (Mouse wheel, touchpad, smartphone screen, and voice input are also supported.) The LLM receives the entire content of the chapter and tries to answer questions based on the chapter's content, which reduces the chance of hallucination to the minimum. (Though not to 0%, of course, but near it.) This way of content consumption is known as **active reading,** a strategy for engaging with a text to improve comprehension and retention by consciously interacting with the material. The goal is to move beyond passive reading to a deeper understanding of the text and to remember key information more effectively. The registration on ChapterPal is via the waiting list. This is to avoid unexpected load spikes and cloud charges. Usually, it takes less than 24 hours for me to activate a user. Give it a try and let me know what you think. The next stage is finishing the content ingestion pipeline, which will automatically convert high-quality content from sources like HTML, PDF, and LaTeX into Markdown. Obviously, only those pieces whose licenses allow creating copies. ChapterPal has its own collection of textbooks and articles on AI, machine learning, and data science topics. If you don't find a piece of content you would like to read in ChapterPal's collection, a Chrome extension, ChapterPal Uploader, allows you to upload any PDF or HTML page to ChapterPal in one click. The content is only available for you to read to avoid the possibility of copyright infringement. I hope you enjoy using it as much as I enjoy building it.

BURKOV

81,258 views • 10 months ago

I've been building a music player with Next.js for fun. Here's a quick demo of how it works (it's open source!) • Demo: • Code: If you want to learn more about how it's built, here's more details ↓ I'm using Postgres (with Drizzle) to store information about the songs and playlists. Audio and image files are stored in Vercel Blob (object storage), and the URLs are then referenced in the database. For the UI, I'm using shadcn/ui (so Tailwind CSS and Radix). This made it easy to copy/paste in some nice components, like the dropdown menus. I built the entire first version of the UI in v0 and then iterated from there, feeding it my Drizzle schema as a source in the project and having it scaffold some of the boilerplate for me: I added support for keyboard navigation (using arrow keys) or vim motions (j/k to go up/down, and h/l to go between playlists and tracks). Also, space to toggle the now playing song, and / to focus the search input. The search function has a nice utility to highlight the currently searched text on the page in yellow. Then, I was exploring how to pass metadata from my application to macOS or iOS. Turns out there's an API for that – MediaSession. Web apps can share metadata about what media is playing (title, artist, album artwork) and sync play/pause/seek with system media controls. Works across modern browsers — even integrates with iOS dynamic island and shows up on lock screens: I set up my app like a PWA – it has a manifest.json file, so it can be installed to my iOS home screen or added to my dock on macOS. On iOS, it then uses the full screen height `100dvh` (dynamic viewport) and has padding on the bottom for the safe area with the `env()` CSS function. Finally, I was able to use the Vercel AI SDK in a script to clean up the metadata on audio files I downloaded from YouTube. Bonus: I even was able to dogfood the React Compiler, which helped me fix a performance bug! That's all! It's fun to make personal software:

Lee Robinson

118,242 views • 1 year ago

A little look behind the scenes: Many of you asked how I handle switching from SwiftData & CloudKit to Firebase and a different way of sharing entries with your friends. Previously, there was no need to create an account and the app just used the user's iCloud ID in the background to "sign up". Why did I do this? SwiftData is really nice to use but after working with it for over a year now, it feels still quite unstable, buggy and errors are super hard to track. Same goes for CloudKit and the CloudKit sharing that I implemented. For a growing app, in my opinion, it's just not feasible to use because errors happen regularly, you don't really know if the data is stored (and shared) correctly and the documentation on CK is just bad. I have big plans for this app (reacting to your friends' entries, Apple Health integration, tagging multiple friends in entries etc.) and all of it is now much simpler - and faster to build. The Migration part When an existing user starts the app, a little migration sheet with a couple of steps opens which can't be dismissed. The goal is that the user maintains all entries and can use the app like before :) Steps: 1) Create an Account using Apple Sign In and Firebase Auth 2) Migrate your user profile (profile picture etc.) and all your friends from SwiftData to Firestore. Here I basically iterate over all the data that is there, run a custom conversion for all the fields (some things need to be stored differently) and save them. Profile Images are now saved in Firebase Storage 3) Migrate all entries to Firestore the same way. Images and Voice Notes are now saved in Firebase Storage (that might take a bit of time, video is sped up) 4) After that, migration is basically done and the user can use the app like before. In the settings, the user can now log out or delete the account, things that weren't possible before. If for any reason the migration has to be done again, this can be triggered from the settings as well. How do I decide if a user needs to migrate? Previously, I saved the user's profile in SwiftData under a specific key (simplified). If the app finds something under this key, user is prompted for a migration. New users don't have to run through the migration. They simply start fresh with the Apple Sign in in the onboarding like seen in the referenced post. How does sharing entries now work? For sharing, I create deeplinks using branch . io now. These links basically work like every other Invite Link you've received or created. The friend opens the link, can accept your invitation and now both of you are connected as buddies. Previously using CloudKit Sharing, this process took two steps - each user had to share a link to give access to their entries. The process now is much simpler and better understand for the user. Developing the migration Tbh I thought this would be way quicker. In the end it was > 100 commits since I had to basically change every screen in the app. Working with SwiftData is quite different to how things work with Firebase (and most other frameworks), so every interaction where data was involved had to be reworked. Now the app runs on a combination of Firestore, Cloud Functions and Firebase Storage. In terms of memory, the app feels notably faster and only needs around 1/4 of the memory bandwidth it needed before. Let's see if it will be worth it! All in all super happy with the new flexibility for the app and not being limited by the technology anymore 👀

Fabian Gruß

26,781 views • 1 year ago

🧵1/ I’m back from a short trip on a social network with photographs and videos from Gaza, with some brief personal conclusions. First, it is quite clear that there are those in Gaza who know how to make extensive use of the networks and social media for the purposes of emotional extortion and also false propaganda, exactly like they live streamed the October 7 massacre and somehow turned it into “justified resistance”, they use quite a few Western buzzwords such as “genocide”, “occupation”, “oppression” and “Zionism”- and this is the simplest way to distinguish between Hamas propaganda posts and authentic people in Gaza, whom I’ve known all my life, who use much simpler words and not this indoctrinated manipulation bullshit. I was looking for THESE authentic people - the real voices. And I think I found a few. So first of all, what do I think there is NOT happening in Gaza? There is no genocide and no famine. Yes, there is quite a lot of suffering, shortages in some places and lack of decent shelter etc. And there’s a lot of cynical exploitation of it for propaganda purposes. I’m saying it because you can see children are getting payed for it, you can see the absolute control of distribution and you can see how basic needs and the way out is blocked with money- Israel is not taking money for any of it, the West is sending free aid, so the money is demanded by those who want to control the suffering and use it. You should know, that before the war - there was already quite a lot of very poor population in Gaza, but now the economic difficulty has greatly increased for two main reasons: one, the war damaged many sources of livelihood and the second - a major source of livelihood was working in Israel and this option was closed for a long time now, since October 7, and is now much less operational. So in fact the main source of income is donations and funding from Hamas for control purposes of course. This is how, among other things, Hamas uses the population to "work for them", and also to smile or cry for the cameras on demand. Now on the issue of claimed famine- famine is when there’s NO food and water, enough for the population. As I said, my personal conclusion is that there is no shortage of food and water in Gaza at all. The problem is that the food is not distributed equally, it is controlled by those who are powerful (usually Hamas operatives) and then sold at high costs to the population without them having sources of livelihood. Food that should be distributed for #free is actually sold at a high price, the shelter tents are also paid for and the exit from Gaza is particularly expensive (5000$ that are paid to the transporters). There are quite a few who refer to Gaza as a "prison" but Gaza is not a prison, Hamas has simply created a situation where it is very expensive to leave it - and thus the poor and the new poor are completely dependent on Hamas and other clans (Hamulas) in everything to do with food, shelter and exit, and they play with them as pawns. If Gaza is a prison - then it’s because Hamas are imprisoning it. All the suffering in Gaza started because of #Hamas it continues and increases because of #Hamas, but the main point is that #Hamas is also the one factor that can stop it. If Hamas surrendered unconditionally and released the Israeli hostages, as done in any war in which you are forced to admit defeat - they would truly liberate Palestine. Remember- this is a war that #Hamas started, a war that on Oct. 7 was celebrated in the streets of Gaza. But #Hamas prefers to keep the Palestinians captive so that it can showcase suffering (which they create) and use it as a tool for financial donations, manipulation of the West and a messianic jihadist war that will never end for them. If the West doesn’t wake up to this ongoing manipulation- they will never help the Palestinians in Gaza or in general, and they will bring this jihadist chaos to their doorstep From Gaza >

Gal G., Adv 🇮🇱

2,695,728 views • 2 years ago