Sitemap

Building a web app with Cursor and good vibes

7 min readMar 25, 2025
https://anything-league.vercel.app/

Nine years ago, I built my first web application using Ruby on Rails and deployed it on Heroku. I still remember learning the basics of the model-view-controller (MVC) pattern, running migrations, and making everything look just okay with Bootstrap.

At that moment, I never imagined that less than a decade later, it would be possible to build similar web applications just by texting an AI, “Hey, can you make an app that does XYZ?” the same way friends and family pitch me ideas when we are hanging out. Even just a few years ago, when the first LLMs started solving novel Leetcode-style problems, I didn’t fully believe that AI tools could become more than just an intelligent autocomplete. But its now 2025, and for the first time since I deployed my first Ruby on Rails app, my workflow for building web applications has completely changed.

An Afternoon of Vibes

Over the past month or two I have tried out a handful of “vibe coding” IDEs including Copilot Edits, Windsurf AI, and Cursor. These agentic tools do more than just write lines of code — they can search your codebase for references, develop a plan of action and then execute multiple steps including modifying code, recommending CLI commands, and asking for manual inputs.

This weekend, I decided to spend some time building a web application called “Anything League” with the help of an AI coding tool. I am a big fan of Music League, an app where friends compete by submitting songs that match a weekly theme, then vote on the best choice. I thought it would be fun to create a similar social competition — but where the submissions could be anything, not just songs.

On Sunday I sat down on my couch, turned on the March Madness NCAA basketball tournament, and opened my Cursor code editor. Every 30–60 seconds, I checked its progress — confirming new features, tweaking styling, and prompting it to build the next piece using simple user-story-like instructions. Over the course of one afternoon spent mostly watching basketball, Cursor was able to bring Anything League to life.

Working with Cursor

My initial prompts to Cursor were using the “Ask” mode. I first asked it to give its best general recommendations for building and shipping a web application as quickly and cheaply as possible. Cursor recommended a stack including:

  • NextJS, React and Tailwind for the frontend
  • Supabase and Vercel for database storage and hosting

These all sounded good to me, so next I prompted cursor to recommend a set of models to represent the key concepts in my application:

The application is called “AnythingLeague.” It is a website where users can create a “league”, then other users can join that league. The league has a configurable cadence for submissions (weekly by X day at N time) and votes (weekly by Y day at M time). The league also has a configurable “theme” for each week. Each week, users must submit an item related to that week’s theme by the submission deadline. Once the submission deadline is reached, a “playlist” is created where users can see all the submissions with their submitter anonomized. For now, the item can be a url, image, or text. Each user can assign a preconfigured number of upvotes or downvotes to the week’s submissions by the voting deadline. After the voting deadline, the vote tallys are revealed, and users gain points for each upvote and lose points for each down vote. After the preconfigured number of weeks, the league ends and players are ranked by total votes.

I also added an addendum to track how much context Cursor was keeping track of:

From this point on, end each message with “Go AnythingLeague!” to prove you have not forgotten any context.

As soon as I stop seeing “Go AnythingLeague!” after the AI output, I know I should re-supply old context.

Once I had confirmed the models and fields Cursor recommended for my app, I swiched from “Ask” mode to “Agent” mode and Cursor got to work. I had Cursor start by building the user authentication, then built out each page of the application. Here is an excerpt of some of my conversations with the Cursor chat:

In these first two screenshots, we can see Cursor build out a new feature from scratch.
After initially building the feature, manual testing sometimes reveals additional issues that Cursor is happy to fix

When it was time to deploy the app, Cursor was also able to walk me through the steps required outside the code editor. When possible, Cursor will also provide CLI commands to handle creating a git repo, installing libraries, and more.

One of the largest time saves, by far, was handling the styling and UI/UX of the app itself. With previous side projects, I’d say at least 30–50% of my time is just spent adjusting flexbox to get everything correctly positioned on the page. With Cursor, making everything look nice took about 60 seconds total — I literally just gave it a screenshot of the original Music League website and asked it to use similar styles, and it did!

Disclaimer: I only allowed AI to use the exact copy here because Anything League is a personal project, I would not use this approach for commercial ventures.

All the code for Anything League can be found here on Github.

Note: I cannot access my prompt history because the cursor history export tool is broken, so some of the earlier prompts are recreated from my best recollection.

Final Thoughts

Working with Cursor was generally painless, and I was impressed by how quickly it recognized and fixed its own mistakes. There were a few occasions I needed to intervene more actively:

  1. A few times, Cursor got stuck in a loop while trying to fix an error message — it would try approach A, then try approach B, then try A again. In these cases I either had to provide it more context of “you already tried A and it didn’t work because XYZ” or fix the bug myself.
  2. Cursor would occasionally make sneaky edits to areas that were not expected. For example, when I asked it to add the ability to add comments to submissions, it removed the display of the votes in order to “keep the UI clean and intuitive.”
  3. Early on I would either waste time adding too much detail to the prompt (i.e. change this line to do this and this differently) or not give enough detail (then being forced to revert unintended changes). After awhile, I started to find the “sweet spot” for prompting.
  4. Sometimes Cursor would recommend a massive shift in approach, or make a huge edit to a file. In those cases, I would often have to reject the proposed changes and come up with an alternative approach manually or change the acceptance criteria of my request entirely.

By default, Cursor also organizes code in a manner that is not ideal for human editors and breaks a lot of clean code principles. I’m sure I could have prompted Cursor to follow clean code patterns more diligently, but I was trying to intervene with Cursor’s process as little as possible.

  1. Cursor rarely broke out subcomponents and preferred to use lots of branching within a single file — my “round details” page ended up nearly 1000 lines long, for example.
  2. Cursor was happy to query the database as much as needed to fulfill the prompt criteria. In the fetchRoundData method, for example, Cursor fetches data from the rounds table 3–4 separate times rather than pulling all the data at once and running various reductions in memory.
  3. Every AI code editor I have used loves adding comments, likely by design, and Cursor is no exception. The best code is generally self-documenting, and I do think the Cursor code was generally very intuitive to read, so most of the comments feel unnecessary.
  4. Cursor taught me a lot about Supabase and added constraints, indexes, and enabled Row Level Security on basically every table in the initial Supabase schema set up. This made changing the schema way more tedious than I am used to for a personal project, but is a good best practice to adopt!

Nine years ago, building my first Ruby on Rails application took me multiple weeks, countless hours searching on Stack Overflow and digging through the Rails docs, and numerous Office Hour visits.

As recently as last year, building and shipping an application with a decent UI and feature set took me at least a weekend, if I was using a tech stack I had familiarity with.

Today, I built and deployed an app in an afternoon while watching basketball.

This post was lightly edited by AI. Big thank you to my coworkers at Panorama who have provided lots of insight into AI coding tools and to my wife, Monica, for all her support!

--

--

Responses (1)