After a recent boba run with my husband, we found ourselves trying to remember which drinks we liked and what customizations made them perfect at each shop we visited. With endless combinations of sugar levels, ice ratios, and toppings, we wanted a simple way to track our preferences and remember what worked (or didn’t) for next time. An app seemed like the perfect solution.

I had previously tried building something similar using Replit but always ran into issues when integrating the Google Places API. Late one night (or rather, very early morning), I decided to give Cursor a try. I had been using it at work and on other personal projects, so I was curious how it would handle an app from scratch. Within just one hour, I had the first version of Boba Buddy: a mobile app that helps us keep track of our perfect boba orders at every shop we visit.

In this post, I’ll walk through how the first version of the app came together in just one hour.

What is Boba Buddy?

Boba Buddy is a React Native app built with Expo that lets users:

  • View a list of nearby boba shops
  • Add notes on drink orders and preferences
  • View and edit past entries

One Hour of Development, Step by Step

At 4 am, after being awake for a couple of hours and unable to fall back asleep, I decided to get up and be productive. I opened Cursor and started from scratch. The next hour was surprisingly productive. Here’s how the first version of Boba Buddy came to life, one step at a time:

Laying the Foundation

I generated an Expo app and asked Cursor to create the core screens:

  • A home screen with a list of boba shops
  • A detail screen for each shop where users can add their drink order and notes

Persistent Notes

Next, I asked Cursor to add the ability to:

  • Save notes locally using AsyncStorage
  • Edit existing notes

Styling Pass

Then I made some styling updates. Without providing any designs or design specifications, I asked Cursor to modernize the UI and give it a theme that felt more like a boba app. It generated the following color scheme:

  • Primary brown (deep brown for text)
  • Secondary brown (milk tea brown for accents)
  • Background (soft cream color)
  • White (for cards and containers)
  • Border/Separator (light brown)

I also revised some of the default copy it had generated to better reflect the app’s purpose.

Real Data with Google Places

Once the UI felt solid, I focused on replacing the hardcoded shop data with real, location-based results. This was the part I had previously struggled with in Replit. I initially ran into an issue using Cursor as well. When I asked it to add Google Places API integration, it also modified the app’s structure by adding tabs and a splash screen. I reverted those changes and asked it to proceed one step at a time with me manually testing after each step:

  • Setting up location permissions: Cursor suggested using expo-location, Expo’s official location package that handles retrieving the user’s current location and managing permissions.
  • Fetching nearby boba spots using the Google Places API: I used the @googlemaps/google-maps-services-js package to make API calls.
  • Replacing the static list with dynamic search results: After asking Cursor to console log the results first to confirm we were receiving the expected response from the API.
  • Improving location accuracy
  • Adding error states

I noticed Cursor was better at Replit in helping me debug by giving me specific instructions like making sure the APIs I needed are enabled and adding in console log statements to determine that the API key is valid.

Seeing the app populate with actual nearby shops was a major leap. It instantly made Boba Buddy feel usable and real.

Boba Buddy App Screenshot

The screenshot at this point shows real results, but the Places API query hadn't yet been refined to return only boba shops, some coffee shops are included. Tweaking the query to better filter for boba-specific places was the only piece of code I wrote myself that didn't come from Cursor.

A Bug in the Header

I ran into a small bug: when navigating to a shop’s detail page, the header just showed the word “Shop” instead of the actual shop name. I asked Cursor to update the copy, but I still saw the word “Shop” afterward. I then asked Cursor to help debug the issue. At first, its suggestions didn’t fully resolve the problem, so I had to iterate a couple of times, clarifying what I was seeing before we landed on a working solution.

The issue turned out to be related to how navigation parameters were being passed between screens after switching from static data to dynamic data. The fix involved updating how the component accessed the shop data. We needed to properly retrieve the shop name using the shop ID that was passed through the navigation parameters, rather than relying on the previous static data.

Customization UI and Updating the Data Model

Originally, each note was just a text entry. But as I played around with the app, I realized I wanted more structure. So I asked Cursor to update the form to include:

  • Drink name and freeform notes
  • Sugar and ice level
  • A hot/cold toggle for temperature

Cursor suggested using sliders for the sugar and ice levels, and I gave feedback on the layout. These changes made it easier to enter detailed preferences quickly, and they also made me think about adding a future feature to snap a photo of the order. That’ll be for the next iteration.

Development Patterns That Emerged

Iterative, Intentional Refinement

Instead of planning everything upfront, I took an iterative approach:

  1. Start with the basics
  2. Play around with the app (though I hadn’t taken it out for real-world testing yet)
  3. Spot missing features
  4. Iterate

This approach allowed Cursor to focus on small sections of functionality at a time, making each step easier to debug and refine.

In my experience, it’s far more effective to ask AI to work on one small piece of functionality or a specific improvement at a time, rather than trying to tackle everything all at once. It keeps the process more manageable and leads to better results.

Hardcoded to Dynamic

By starting with hardcoded data, I could test the UX early and focus on the user flow. Only after that was working did I introduce dynamic, location-based content. This pattern—starting static and layering in complexity—kept things manageable and productive.

Final Thoughts

By the end of that early morning, one-hour session, Boba Buddy was more than just a prototype. It was something we could actually use on our next boba run.

What made the experience especially remarkable was that this only took an hour to put together and I didn’t write a single line of code, aside from tweaking the query parameters used in the Google Places API to return more accurate boba shop results. Everything was built through iterative conversations in Cursor, which let me focus entirely on the user experience. Of course, I reviewed the libraries added to the project and inspected the code at each step to ensure it made sense and aligned with what I wanted.

This first version of Boba Buddy came together in a very short time, but there’s still plenty I want to improve, especially the user experience on the detail page.

Stay tuned for future posts, where I’ll share how I continue to iterate on Boba Buddy, refine the UX, and expand the feature set, all while continuing to use Cursor to streamline development.