Previewing localhost on other devices
By Salvatore Castellitti, developer of CodeMote · 22 July 2026
Three situations keep producing the same problem for me: checking a layout on a real phone instead of devtools' responsive mode, showing work in progress to someone without deploying it, and testing webhooks that need a public URL to call. In all three cases the thing I want to show runs on localhost on one machine, and the thing that needs to see it is somewhere else.
The old fix is running ngrok or cloudflared next to the dev server and pasting the URL around. That works, and I did it for years. The annoyances are small but constant: another process to remember, a URL that changes every restart, and no connection to the editing side, so you end up alt-tabbing between a terminal, the code, and two browser tabs.
My current setup handles it inside the workspace I'm already using. The dev machine runs the CodeMote host, and the browser workspace has a Ports panel: it shows what's listening on the machine, forwards a port in one click, and previews it in a pane next to the editor.
Setup
On the dev machine, in the project:
npx codemote-cli start npm run dev # or whatever serves localhost:3000
Pair a browser at the connect page with the code the CLI prints. Open the Ports panel; the dev server shows up in the list. Forward it and you get a public URL.
Each forwarded port gets its own quick tunnel, outbound from the host machine, so this works without touching the router or firewall. Same mechanism as the workspace connection itself.
Testing on a real phone
Devtools' responsive mode gets viewport sizes right and misses everything else: scroll behavior, the on-screen keyboard shoving your layout around, safe areas, whether buttons are actually tappable with a thumb. Every time I think a layout is done in devtools, the real phone finds something.
With the port forwarded, I open the URL on my phone's browser and I'm looking at the live dev server. Save a file on the desktop, refresh on the phone. The last bug this caught for me was a fixed bottom bar that iOS Safari's toolbar covered completely; responsive mode had shown it as fine.
Showing work in progress
For client calls I used to deploy to a staging URL first, which meant work stopped fifteen minutes before every call. Now I forward the port and paste the URL in the chat. The useful part is having the editor in the same window as the preview: when the feedback is "can that heading be shorter", I change it while we're still talking and they refresh. Not every kind of feedback works this way, but a surprising amount of design feedback does, and it saves an email round-trip each time.
Webhooks are the same mechanism with a machine on the other end. Stripe or GitHub needs a URL it can reach, the handler runs on the dev machine, the forwarded port connects them, and I watch the request arrive in the terminal panel.
Caveats, because there are some
The quick-tunnel URLs rotate when the tunnel restarts. Fine for a call or an afternoon of testing; wrong for anything someone will bookmark. If you need a stable URL, that's what the paid ngrok tiers or a reserved domain are for; the tunnel comparison post goes into the options.
While a port is forwarded, anyone with the URL can reach it. The URLs are long and unguessable, but they're still just links, and links get forwarded. My rule: forward the app, never the database admin UI, and unforward when done.
And if your app builds absolute URLs from localhost, requests through the tunnel will break in ways that look mysterious for about five minutes and then are obvious.
None of the individual pieces here are new; tunnels have existed forever. What changed my routine is having the port list, the preview and the editor in one place, so checking something on a phone stopped being a context switch. Beta signup is on the homepage.