localhost is not the internet
http://localhost:3000 means "this computer." Sending that link to a friend sends them to their own machine, where nothing is listening. This trips up everyone once.
To put your app somewhere a stranger can reach it, four things have to exist away from your laptop: your code, a machine to run it, your configuration, and your data.
Where it can live
If you built on Replit, Lovable or Bolt, there is a deploy button and the platform handles all four. Use it. The rest of this lesson still matters, because the differences below still apply.
If you have your own repository, the common hosts are Vercel, Netlify, Cloudflare and Render. All connect to GitHub and redeploy each time you push. All have a free tier that genuinely runs a small app. Pick one and stop researching; changing later takes an afternoon.
Your database is separate from your app host. Neon, Supabase and PlanetScale all have free tiers. The database on your laptop is not the database in production, and this catches people: the tables you made locally do not exist in the new one until you run your migrations against it.
The dev server lies to you
What you run locally is a development server. It is patient, forgiving, and configured for your convenience. Production is a build. Things that work in one and not the other:
Filename case. Your Mac or Windows machine treats BookingCard.tsx and bookingcard.tsx as the same file. The Linux server that runs your app does not. An import with the wrong capitalisation works locally and fails in production with "module not found."
Environment variables. Your .env file never left your laptop, on purpose. Every value in it — database URL, API keys — has to be entered again in the host's settings panel. This is the single most common deployment failure.
Hardcoded localhost. A fetch("http://localhost:3000/api/...") sitting somewhere in the code. Fine on your machine, dead everywhere else.
Dependencies in the wrong list. A package installed as a dev dependency that the running app actually needs. The build cannot find it.
Type and lint errors. Dev servers often run through them. Production builds usually stop.
So deploy early, on day two, with an app that barely does anything. A deploy failure on a tiny app is a ten-minute puzzle. The same failure on a big app after three weeks is a bad day.
The stranger is not you
Once it is live, the first real test is a person who is not you. They arrive with no cookies, no session, no seeded data, on a phone, possibly on a slow connection, and they click in an order you did not imagine.
Before you send the link:
- Open the site in a private browsing window. Logged out, empty state. Does it make sense, or is it a blank screen because it assumed you had data?
- Open it on your phone. Actually on your phone, not a narrow browser window.
- Sign up as a brand new user, all the way through. This path is the least tested and the most important.
- Try one thing wrong. Wrong password. Empty form. Refresh mid-way.
- Press the browser back button after doing something. This breaks more AI-built apps than any other single action.
Watch what happens next
Once strangers are using it, you need two things you probably do not have yet.
Logs. Every host has a page showing errors from the running app. Find it now, while nothing is wrong, so you know where to look when something is. A user saying "it didn't work" plus a log entry from that minute is a solvable problem. Either one alone is not.
A way to be told. Something like Sentry, free at small volume, emails you when your app throws an error. Without it your error reporting is people bothering to complain, and most will not.
One more thing
Add a way for people to reach you — an email address in the footer is enough. The first stranger who hits a bug will otherwise leave silently, and you will never know the bug exists.
Before you move on