Skip to content
Vibe Coders

Moving From Bolt to a Real Git Repo Without Losing Your Work

The export, commit, redeploy workflow for Bolt projects that outgrow the browser IDE. What to rename, what to restructure, and the conventions to add so future devs, human or AI, can work on it.

By WitsCode10 min read

There is a specific moment in a Bolt project when the browser IDE stops feeling like a superpower and starts feeling like a cage. Usually it is when you want a second person to contribute, or when a single prompt breaks three things at once and you realise there is no local history to roll back to. Sometimes it is simpler than that. You want VS Code, you want a terminal, you want to run scripts that are not a chat message.

This is the migration guide we walk clients through when they cross that line. The Bolt export is the easy part. What matters is what you do in the first hour after the export, because the shape you leave the repo in is the shape future developers and future AI agents will inherit. Do it well and anyone can pick the project up. Do it carelessly and you have a zip full of unlabelled components that nobody, including Claude or Cursor, can reason about.

Why the Bolt export is the easy part

Bolt runs your project inside a StackBlitz WebContainer, which is a real Node environment running in the browser tab. That container is great for speed of iteration. It is also a slightly different beast from a normal local dev environment. Paths, environment variables, and lockfiles all behave a little differently. So the export is not just a file copy, it is a translation.

Bolt gives you two ways to get the code out. The first is the Download button in the top-right toolbar, which hands you a zip. The second, and the one you should prefer, is the Connect to GitHub integration in the same toolbar. That one authorises Bolt to push directly to a new repository on your GitHub account. You get a working repo in one click, and the initial commit is already there.

The reason this is the easy part is that Bolt has already solved the transfer. The hard part is that Bolt has not solved any of the things a mature repo needs, which is everything after the transfer. No conventions, no docs, no environment template, often no complete gitignore. You own that work. If you skip it, the repo is technically on GitHub but practically unworkable.

Getting the code out cleanly

Before you click Connect to GitHub, take five minutes and look at what you are about to push. Open the file tree inside Bolt and scan it with tired eyes, the way a stranger would. You are looking for three specific problems.

The first is a node_modules folder sitting at the root. Bolt sometimes materialises it inside the WebContainer, and depending on how you exported, it can end up in your zip or your first push. It should never be committed. The second is a .env file at the root with real values in it. Bolt frequently writes environment variables during a chat session, and some of those values are API keys you pasted to test something. Pushing them to a public repo is a bad afternoon. The third is large binary assets, usually placeholder images in the megabyte range, that will bloat the repo forever because Git does not truly delete history.

If you find any of those, fix them before the first push, not after. A .gitignore at the root containing node_modules, .env, .env.local, .env.*.local, dist, build, .next, .vercel, .netlify, .DS_Store, coverage, and *.log covers the common cases. If Bolt generated one, open it and confirm each line is present. Bolt's default gitignore is improving but historically has missed at least two of these.

Once that is done, use the GitHub connect flow. Give the repo a name that matches the product, not the Bolt project ID. Choose private unless you have a specific reason to make it public. The initial push will include everything in the working tree that is not in your gitignore, so the ten minutes you just spent sanitising is ten minutes of future regret you avoided.

The first restructure pass, folder shape and naming

Open the fresh repo in VS Code. This is where most vibe-coded projects reveal their true structure, which is usually a flat src folder with every component at the same level, a mix of file name cases, and a few orphan files whose purpose you no longer remember. That is normal. Bolt optimises for getting things working, not for being readable by a second person.

Pick a folder shape before you touch anything. For most React projects from Bolt, the shape that works is src/components for reusable UI, src/components/ui for primitive atoms like Button and Input, src/features or src/sections for page-level composed blocks, src/lib for utility functions and API clients, src/hooks for custom hooks, and src/pages or src/app for routes depending on whether you are on Vite or Next. If you are on Next App Router, the app folder is already there, so you are only reorganising within src.

Rename files to one case convention. Components in PascalCase, everything else in camelCase or kebab-case but consistently. If Bolt gave you Header.jsx and footer.jsx and navBar.jsx in the same folder, pick one rule and apply it across the repo. This sounds cosmetic. It is not. Future AI agents use file names as a strong signal about what a file contains, and inconsistency makes their suggestions worse.

Extract anything that looks like a hardcoded API URL or secret into src/lib/config.ts or src/lib/api.ts. Bolt tends to inline fetch calls inside components during rapid iteration, and those inline calls accumulate hardcoded base URLs that point to wherever you were testing that afternoon. Consolidate them. One place to change the backend URL is worth an afternoon of debugging later.

Do not rewrite logic during this pass. Rename, move, and extract only. The goal is a repo that still runs exactly as it did inside Bolt, just with its furniture arranged. Run the app locally after the restructure and confirm nothing broke, then commit with a message like "restructure, no behaviour change" so future-you can find the boundary.

The .env.example and gitignore discipline Bolt skips

Every environment variable your app reads should appear in a file called .env.example at the root of the repo, with the key present and the value blank or a clear placeholder. No real secrets. This file is committed. Its purpose is documentation. When a new developer or a new AI agent clones the repo, .env.example is how they know what environment to provide.

The pattern is simple. If your app reads VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY and OPENAI_API_KEY, your .env.example contains all three lines, with either empty strings or a comment describing what the value should be. Your actual .env is gitignored and holds the real values. New devs copy .env.example to .env and fill it in.

Bolt almost never generates this file for you. Add it as part of the migration. While you are in there, also add a .nvmrc file at the root with the Node version the project expects, a .editorconfig with basic indentation and newline rules, and a prettier config if you have opinions about formatting. Run prettier across the whole repo once and commit that as a single formatting pass, separate from real work. It makes every future diff readable.

One more piece that pays off later. Add a scripts section to your package.json that covers dev, build, lint, format, and typecheck even if some of those commands do not exist yet. Stub them as echo statements if you have to. The reason is that CI configurations, deploy hooks, and AI agents all look for standard script names. Having them defined, even minimally, makes the repo look like every other repo, which is exactly what you want.

README and CLAUDE.md so future agents do not wreck it

The README that Bolt ships is whatever came with the starter template. Replace it. A useful README for a migrated Bolt project has six short sections. What the app does, in two sentences. The stack, listed as a few bullet-free lines in prose. How to run it locally, as three or four commands. Where environment variables come from, pointing at .env.example. The deploy target, so nobody has to guess whether this goes to Vercel or Netlify. And a link to whatever issue tracker or notes doc you are using. That is enough. A README is not a book.

CLAUDE.md, or AGENTS.md, or .cursorrules depending on which tools your team uses, is the more interesting file. This is where you write down the conventions a future AI agent needs to respect when editing the repo. Think of it as a constitution, not a memoir. The things that belong in it are architectural rules that are not obvious from reading the code. Which state management library you are using and are not allowed to replace. Which styling approach, Tailwind classes versus CSS modules, is the one rule. The naming convention you just enforced. Where new routes go. How to add a new API endpoint. Which test runner to use and where tests live. How commits should be written.

Keep it short. Two pages at most. Every line in CLAUDE.md is a line the agent will prioritise over its generic training, and a bloated file with contradictions is worse than no file at all. The test of a good CLAUDE.md is that someone could read it in four minutes and then make a correct change to the repo. If you find yourself writing essays, you have drifted from constitution into memoir.

This file compounds. Every real bug you fix that was caused by an agent breaking a convention should end with you adding one line to CLAUDE.md so the same bug does not recur. That is how the document earns its keep.

VS Code devcontainer and the reproducible dev environment

The last piece of the migration is a .devcontainer folder. This is the thing that takes your repo from "works on my machine" to "works on any machine that has VS Code and Docker." It is also what makes GitHub Codespaces work, which gives you a browser-based fallback very close to the Bolt experience you just left, except on a repo you fully own.

The minimum setup is a .devcontainer/devcontainer.json file that specifies a Node image matching your .nvmrc, a postCreateCommand that runs npm install, and a list of VS Code extensions to preinstall, usually Prettier, ESLint, Tailwind CSS IntelliSense, and GitLens. Port forwarding for 5173 if you are on Vite, or 3000 if you are on Next, so the preview works when you open the repo in Codespaces.

The payoff is that any contributor, human or AI assistant running in a cloud sandbox, gets an identical environment on first clone. No "did you npm install," no Node version mismatch, no missing extension. For a vibe-coded project that started in a browser and is now growing a real team, the devcontainer is what keeps the onboarding experience close to what Bolt offered, without giving up ownership of the code.

Branch strategy after you leave the browser

Bolt trains you to iterate on one timeline. Everything is main, every change is live, every prompt is a commit. That model is fine for a prototype nobody else depends on. It becomes dangerous the moment real users hit your deploy, or the moment a second person starts contributing.

The switch is simple. Treat main as always deployable. Every change happens on a feature branch named for what it does, like fix-checkout-button or add-stripe-webhook, and merges to main through a pull request. Even if you are the only person on the repo, the PR flow gives you a preview deploy on Vercel or Netlify, a diff you can read before you merge, and a record of what changed and why. Those three things together replace most of what a senior teammate would provide, and they cost you maybe sixty extra seconds per change.

The one extra rule that matters for vibe coders is this. When you are letting Claude or Cursor make a multi-file change, do it on a branch, not on main. Agents make mistakes that are hard to see in a single glance, and a branch with a PR is how you get a second look before the mistake is live. This is the discipline that separates a Bolt project that survived for six months from a Bolt project that had to be rebuilt from scratch.

When to hand this off

Some teams do the migration themselves and do fine. Others get through the export, get stuck on the restructure, and ship a repo that technically lives on GitHub but nobody wants to touch. That is the point where we usually get the call.

A WitsCode repo-hardening engagement takes a Bolt export and does the full pass. Folder restructure, naming conventions, gitignore and env.example, README and CLAUDE.md, devcontainer, CI with lint and typecheck on every PR, preview deploys, and a short video walking your team through what changed and why. Then we hand it back. The next feature you ship, or the next AI agent you point at the repo, starts from a clean, documented base rather than a browser export.

If you are staring at a Bolt zip right now and do not know which file to open first, talk to us > and we will scope the migration. Most projects are a two to five day engagement, and the outcome is a repo that behaves like something a senior engineer built, which is exactly what you need before the next phase of the product.

Get weekly field notes.

Practical writing on shipping products, straight to your inbox. No spam.

Need help with this?

MVP Development

We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.

Talk to us

Want to discuss vibe coders for your business?

Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.