Playing hard with Vercels V0, and breaking it

This is mind-blowing I feel this This really will be the future of making interfaces for the internet.

Is it just had a 2 continuous day play with Vercels V0.

Vercel have raised a series in funding again over half a billion now.

Githubs co-pilot felt nothing more than a rushed cashgrab to get chatgpt into VScode in a quick way to grab that subscription dollar. it was no better than copying and pasting to chatgpt for free.

V0 isnt quite ready to develop something

is it worth the 20usd a month? well yes.

You can design an application as you go along.

Importing an image

It understood what i want

Its understanding is riddiculously powerful

It feels like talking to an expert front end dev who just understands what you are asking. It’s more than just a normal LLM, it can infer what you are talking about from a description like “the divs that hold the Carbon based” text. It can traverse up through the dom from that string and edit elements

 

WTF it can actually do all that right?!!

give the h2 which has “interloper” in it the id of “player_albumname”, give the p which has carbon based lifeforms in it the id of player_albumartist. add a data attribute to the thumbnails which is data-albumartist. add a data attribute to the thumbnails which is data-albumname. create an array of unique albumnames which you generate randomly based on plant species use this to populate the album data-albumname attributes. create an array of artist names which are constructed of two words, first something mundane, second a noun, this list should populate the data-albumartist attr

Yes sir…

Here are the changes made to implement the requested features:

1. Added `id=”player_albumname”` to the h2 element displaying the album name in the player.
2. Added `id=”player_albumartist”` to the p element displaying the artist name in the player.
3. Created arrays for plant species (album names) and components for artist names.
4. Updated the `albumCovers` useMemo hook to include `albumName` and `artistName` properties for each cover.
5. Added `data-albumname` and `data-albumartist` attributes to each thumbnail div, using the generated album and artist names.
6. Updated the `handleThumbnailClick` function to log the album name and artist name along with the album string.
7. Updated the alt text of the thumbnail images to include both the album name and artist name.

The rest of the component’s functionality, including the mouse movement effect and the player controls, remains unchanged. The styling and layout are also maintained as in the previous version.

This implementation ensures that each thumbnail has unique `data-albumname` and `data-albumartist` attributes, and the player now has the requested IDs for the album name and artist name elements.

It even went beyond what i requested and added this output to my console.log without me thinking about that

How much can it infer what i want from vague instructions?

perefect, now when i onclick i want the data properties in the album to appear on the player. fade out the current player string values and fade in the new values from the clicked album

so it needs to remember the context of the onclick events we added earlier, work out that i want to change the h2 and the p on the player, add the fade transitions, then take the correct data-attrs and get the value and apply those as strings to the h2 and p.

shut up and take my money

It undertands what DOM elements i am referring to by “in the album” and it was able to

Oh and of course the arrays containing the auto generated album and band names were on point.

“sunflower by Floating Valley”
“Zinnia by Dreaming Ocean”

Restructuring the data generation

Eventually i want to connect this up to an API to get the data, so if the data source in this function is already in JSON format thats a simple data conversion job to go from the API format to this.

Up to now the album info was generated on the fly from a couple of arrays of words and strings

const plantSpecies = [
  "Orchid", "Fern", "Cactus", "Bamboo", "Lily", "Rose", "Tulip", "Daisy", "Sunflower", "Lavender",
  "Jasmine", "Ivy", "Moss", "Aloe", "Sage", "Thyme", "Basil", "Mint", "Rosemary", "Chamomile",
  "Dandelion", "Clover", "Poppy", "Iris", "Dahlia", "Marigold", "Zinnia", "Peony", "Chrysanthemum", "Geranium"
]

const mundaneWords = ["Silent", "Whispering", "Dancing", "Sleeping", "Laughing", "Dreaming", "Wandering", "Floating", "Singing", "Humming"]
const nouns = ["Cloud", "River", "Mountain", "Forest", "Ocean", "Desert", "Meadow", "Valley", "Canyon", "Glacier"]

const generateArtistName = () => {
  const mundane = mundaneWords[Math.floor(Math.random() * mundaneWords.length)]
  const noun = nouns[Math.floor(Math.random() * nouns.length)]
  return `${mundane} ${noun}`
}

const albumCovers = useMemo(() => 
Array(COLUMN_COUNT * THUMBNAILS_PER_COLUMN).fill(null).map(() => ({
src: '/placeholder.svg?height=200&width=200',
albumString: generateAlbumString(),
albumName: plantSpecies[Math.floor(Math.random() * plantSpecies.length)],
artistName: generateArtistName()
}))
, [])

 

let’s change that to make a JSON object containing the pre generated data….

rather than an array of plant species and words and stuff i want to have a clear JSON object of the albums with those properties, use that json object to populate the UI

its that easy, i didnt even need to bother to be specific…

const albumsData = [
  { id: "ABC123", name: "Whispering Meadows", artist: "Silent River", cover: "/placeholder.svg?height=200&width=200" },
  { id: "DEF456", name: "Neon Echoes", artist: "Digital Dreamers", cover: "/placeholder.svg?height=200&width=200" },
  { id: "GHI789", name: "Cosmic Lullaby", artist: "Stardust Collective", cover: "/placeholder.svg?height=200&width=200" },
  { id: "JKL012", name: "Urban Legends", artist: "City Soundscape", cover: "/placeholder.svg?height=200&width=200" },
  { id: "MNO345", name: "Quantum Harmonies", artist: "Particle Ensemble", cover: "/placeholder.svg?height=200&width=200" },
  { id: "PQR678", name: "Ethereal Rhythms", artist: "Celestial Beats", cover: "/placeholder.svg?height=200&width=200" },
  { id: "STU901", name: "Neon Nights", artist: "Synthwave Collective", cover: "/placeholder.svg?height=200&width=200" },
  { id: "VWX234", name: "Organic Circuits", artist: "Bionic Orchestra", cover: "/placeholder.svg?height=200&width=200" },
  { id: "YZA567", name: "Lunar Tides", artist: "Gravity Pulse", cover: "/placeholder.svg?height=200&width=200" },
  { id: "BCD890", name: "Temporal Flux", artist: "Chronos Quartet", cover: "/placeholder.svg?height=200&width=200" },
  // Add more albums as needed to reach COLUMN_COUNT * THUMBNAILS_PER_COLUMN
]

The only issue is that now there aren enough thumbnails. But thats an easy fix. Also note that it’s now forgetting that the album names should be based on plant species. the new ones sound cooler though.

One thing to note is this step should be done at the end because generating out the whole array for 100+ albums burns a lot of generation time.

Going beyond what you asked

Generate me some album covers on the fly

I wanted swap out the sample svg placeholder image with an auto generated one, it took the context of the whole conversation around album covers and added an svg generation function which makes SVGs which look like actual album covers with the album name on.

Add some buttons with hover state and some margin between the edges

Hover state it knows to change the icon colour to achieve contrast. The margin it applied to the icons is also the same as the margin between the album covers and the viewport. Thats also intentional because its used specific tailwind classes to achieve that.

fixed top-4 left-4 z-30 flex space-x-4

Notice the search and shuffle icons chosen from the Lucide.dev library.

Furthermore I asked it to add a search bar,

I gave no suggestion about what the buttons should look like apart from the icons and function and the border around the form. The ai decided to round the form.

The quality of the code and Tailwind

The JS output is light and clean, with just the right tailwind classes used, its like an expert.

The levels of understanding to use the right class are insane.

 

Player Controls

i described part of the player functionality expecting to describe how it all worked, like next selects a random track prev scrolls through the previous history and it just did it all based on an intuitive understanding about how player work. wow. just wow

Where it falls down

Gets trapped in loops and forgets, still needs a dev to fix it

<div class="w-full aspect-square flex-shrink-0 cursor-pointer" data-id="4"><img data-id="5" src="/placeholder.svg?height=200&amp;width=200" alt="Album cover HE5QYe" class="w-full h-full object-cover rounded-lg shadow-lg"></div>

Managed to get this to set the data-id to a random string, the function

const generateUniqueId = () => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
return Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('')
}

const handleThumbnailClick = (event: React.MouseEvent<HTMLDivElement>) => {
const id = event.currentTarget.getAttribute('data-id')
console.log(`Clicked thumbnail with ID: ${id}`)
}

 

this is what i was after

<div class="w-full aspect-square flex-shrink-0 cursor-pointer" data-id="hTF6ds"><img  src="/placeholder.svg?height=200&amp;width=200" alt="Album cover HE5QYe" class="w-full h-full object-cover rounded-lg shadow-lg"></div>

notice it has edited the Alt tag instead adding the string there

 

is still in the code but when i attempt to add the

const handleThumbnailClick = (event: React.MouseEvent<HTMLDivElement>) => {
const id = event.currentTarget.getAttribute('data-id')
console.log(`Clicked thumbnail with ID: ${id}`)
}

we revert back to showing data-id=4 and 5 again

 

I cant see evidence in the code that there should be an id

tried changing bg to teal which worked so preview is up to day but other code problem persists

this is probably to do with the inner workings of react adding data-id for some other use. V0 was unable to tell me why this occured but hallucinated and told me it was fixed. I tried coyping the same to Perplexity and it was unable to resolve the issue as well. changing the data-somethingelse attribute fixed the problem. I had a feeling using -id wasnt a good idea when i first did it.

Breaking stuff it already did

Change the parallax effect to only apply on larger screens and make the albums scollable

this broke the was a simple reminder to get it working again

 

External images

Refused to load the image '<URL>' because it violates the following Content Security Policy directive: "img-src 'self' blob: data: <URL>".

the suggested fix was…

To address the Content Security Policy (CSP) issue with loading images from Picsum Photos, we'll need to modify our approach. Instead of directly using the Picsum Photos URL, we'll create a server-side API route to proxy the image requests. This way, we can fetch the images from our own domain, which should comply with the CSP.

but this didnt work.

 

Getting to > 500 lines & splitting into components doesn’t work

Starts to get really slow with processing, it doesnt make mistakes it just gets really slow.

Tried to split out functionality like just the player stuff in one component so i only need to work on one file but it only allows one tab to be open at a time, and it lost a load of functionality in the process of splitting the file up

 

V86 to V2? has it just lost all my work?

I had to roll back to V84 after trying to split the code into separate components broke everything and generated loads of errors. After another command i saw the versioning dropdown go from the high 80s back to v2. Im not able to return to v1 now so it seems half a day of work is gone. Without a backup.

Oh

Help?

This went back to version 87 now? how broken will this recovered version be?!

Well it actually recovered everything minus one fade transition when setting the album on the player.

Once you have gone down the route of having multiple “branches” if you refer to something thats on another branch then it will start working on that branch again.

How to help it

it helps to use dev vernacular and be specific at times

focus on one small thing at once when the file gets large. It is tempting to bundle 4 small UI changes but it will start to drop some.

Telling it the problem might also help…

This one might require some dev knowledge.

After getting repeatedly stuck on removing a 1px halo gap between the album cover overlay and the album cover I dug through the inspector to find a fix myself and told V0 to make it…

Pushing it to the limit

Too many lines

Eventualy the source reached around 550 lines at 90 commits. At this point even a simple request to change the z-index of a div would no longer render and i was presented with error messages.The “fix with v0” button couldn’t correct errors. I got around this by resetting to an older version where the display rendered and forking off to a new chat.

The forking interface is really clunky, its not clear that ‘forking’ creates a new chat under ‘History’.

At this point it starts to introduce bugs like several albums load into the player interface one after another after clicking one album cover.

Getting code out and back in again.

As an experiment to see if the RAM (is it ram? is overloaded with context information given the 90 or so versions i wanted to create a new fresh chat with the same code. I copied the sauce out to Github and then pasted it into a new chat which pasted the code as a file attachment. I was able to pick up from where i left off with a simple description prompt about what the code should do. Easy.

Impossibly hard request

progress ring inside a div around some other stuff, i was running out of prompts so just chucked something impossible in, and it failed to do any of it. its not 100% magic yet.

the ring around the outside of the interloper section should be a rounded progress bar which wraps around the outside of the interloper title holding div and shows the time elapsed of the song being played. add this for the current song with a time elapsed of 65% of 3 minutes. the starting point of the round progress bar should be exactly 0 degrees or the top of the circle. the bar should be lime as shown in the original design i provided at the start. the animation should be the length of the bar, with an easeinout easing effect.

and change the image in the back of the interloper section to this URL https://f4.bcbits.com/img/a0967052848_10.jpg

Running out of prompts and the cost

At 20 USD the cost doesn’t break the bank for being 5X faster, but the product does feel a little rough around the edges with the VCS problems, component splitting errors, loops and clunky interface.

Paying a subscrition gets prompts capped at 100-200 per day depending on the rest of the userbase usage which isn’t really fair, to get 50% less some days just because they oversold the product. I couldn’t get beyond maybe 150 prompts in a “day” that was a 12 hour long session. Most sane people wouldn’t work 10+ hours in a day.

 

the free plan isn’t really enough to do anything of use when a lot are used to fix problems where it gets jammed.

 

Deploying to live

Not sure about the workflow yet

What id like to see added

Diff between versions and a version history in a git gui format. there is a dropdown for version but if i say “undo x i want to see another vertical panel which shows the commit being deleted.

Ability to edit the code in the inspector like an IDE which creates a new version which hasnt been staged.

Work on individual components in isolation and then see them merged into the wider project, this would make the generation time for smaller parts

Cursor and the AI IDE

Currently V0 works on one tsx file

The next step i  will work on is moving this over to Cursor to see if i can

 

The future is coming, fast

Theres still furious debate online about whether AI tools

what was once a prestigious job which took years to

furniture makers were skilled craftsmen, now IKEA pump out 1000 flat packed furniture items a minute.

Corporations will likely have lamen users describe in wizards what app they want to make.

I tried this recently with squarespace, but the outcome was poor. Another exicting app to watch in this space is Relume.io if they can master the styleguide and prettifying the scaffold they are onto a mahoosive idea. also being tied to webflow is a huge limitation for the potential of their product, which is why they are investing in react components too.

Give this a few billion more and a couple of years and this is how all apps will be built

AI is 100% coming for your job

Common belief that the tools will make everyone 100x more productive

Naming no names!

This will make making apps as easy as filling in a form.

Imagine how many average devs can now become product generating wizards

maybe it will allow people to do more complex stuff rather than not having budget to do what they want.

Google CEO talking about arrogant expensive devs

https://www.youtube.com/watch?v=8_2yFCm5sSM

Sauce

https://github.com/sharpi-sh/sleevewall-v0 

 

The product at this point

At around 20 hours spent, 130 ish prompts. Several branches and forks that failed…

https://v0.dev/chat/b/sXLHMrc 

Next steps, Cursor:

At some point move across to cursor to do the bulk of the changes

  • set up WordPress headless with search
    • hook up the search to an async powered dropdown linked to that search
  • create separate component with youtube player functionality
  • Figure out workflow for getting this source into a new Github / Vercel deployment on sleevewall.com, Cloudflare DNS
  • switch out the image generation SVG function to actual album images from a test API endpoint
  • use Cursor to set up a new NextJS project locally and integrate this source
  • connect the album cover list up to WordPress
  • change shuffle so that it loads new albums from the API
Was this article helpful?
YesNo