What nobody tells you is that the 10,000-line file was not the real problem. I opened one in Ahmedabad that still rendered. The real problem was that every new Cursor prompt made the file longer, because the model could not see a module edge. It could only append.
The day we split one folder, the same tool started deleting code. That was the first useful commit in weeks.
I had expected a rewrite. What I got was a filing job. The product did not need a new architecture lecture. It needed a desk that a stranger could sit down at.
How a prototype becomes one file you are afraid to open
Vibe-coded React starts honest. One page. One component. One prompt that says “make the dashboard work.” Then the next prompt adds billing. Then a chat panel. Then a settings drawer that imports the billing table because that was the nearest state.
I have seen this origin story in Pune more than once. The founder is not careless. The tool rewards a single file. Imports stay local. The preview stays green. Nobody names a boundary, so the model never respects one.
According to the React documentation on your first component, a component should do one job you can name. A 10,000-line App.jsx is not one job. It is a week of prompts stacked in the order they were asked.
The cost shows up when you hire the second engineer, or when you ask the model to “add SSR.” There is no place to put the new work. So it goes at the bottom. The file grows. The fear grows with it.
A framework I use when the file is the product
I do not start by renaming folders. I start by drawing three piles on paper: data in, view, and actions out. Then I cut the file along those lines. Documentation from the TypeScript modules handbook is enough here. A module is a seam. A seam is a place the next prompt can aim at.
| Split the file so the next prompt has a door, not a wall. |
TypeScript comes in at the seams, not as a religion. I type the props that cross a folder and the payload that crosses the network. I leave the leftover JSX alone until it moves. A full conversion on day one is how teams stall and then paste the monolith back together.
SSR comes after the first split, not before. Next.js documentation on Server Components is useful once a page has a data function you can lift. It is not useful when the only function is a 400-line useEffect that also draws the sidebar. Lift the data. Then let the server run it. The App Router routing guide is the map for where those files go.

A case I still think about
The Ahmedabad file was a Next.js page that had become a product. Auth, billing, a report table, and a chat drawer lived in one component. The generator had added a fourth copy of the date formatter because it could not find the first three.
We did not rewrite it over a weekend. We extracted the report table first, because that was the only screen a paying customer opened every day. We typed its props. We moved the fetch into a server function. We hung a Playwright test on the path that opened the table.
Then we did billing. Then we did the chat drawer. Each cut made the next prompt shorter. Data from the Playwright run was the only scoreboard I trusted. If the table still loaded, we kept cutting. If it did not, we stopped talking about architecture and fixed the last move.
IndiaNIC’s rescue work on that repo was unglamorous. We deleted more than we wrote. Reported back to the founder in one sentence: the product is now six files a new engineer can open on a call.
| Do not extract a component you cannot name in six words. If you cannot say “report table” or “billing form,” you are slicing paint, not architecture. Leave it until the name is obvious. |
Why does splitting the file make the AI more useful?
Splitting the file makes the AI more useful because the model can finally see a boundary to edit, instead of appending to a wall of JSX it cannot hold in context. A named module gives the next prompt a target. A monolith gives it only the bottom of the file. That is why the same tool starts deleting once folders exist.
What nobody tells you is that this is also why rewrites fail. You ask the model for a new architecture. It writes a second monolith next to the first. You now have two files you are afraid to open.
When should server rendering enter a vibe-coded React app?
Server rendering should enter a vibe-coded React app after you have lifted data fetching out of the view, so a Server Component has a real function to run. If the only fetch still lives inside useEffect beside the markup, SSR will copy the tangle to a new file. Split first. Then move the data up. Then let the server render the shell.
I have seen teams enable SSR as a config flag and then wonder why the client still waterfalls. The flag cannot see your effect. A server function can.
The action plan I hand across the table
This is the order that has survived contact with a real repo. It is not a framework war. It is a filing sequence.
- Record a Playwright smoke on login and the one screen a customer pays for.
- Name the three piles on paper. Data in. View. Actions out.
- Extract the one component a customer opens every day. Type its props. Stop.
- Lift that component’s fetch into a server function. Delete the matching
useEffect. - Repeat for the next named screen. Do not batch four extracts to feel brave.
- Add ESLint only after two modules exist, so the rules have seams to enforce.
- Turn leftover one-off markup last. Most of it should be deleted, not typed.
I keep a longer set of these notes on the IndiaNIC blog. The useful pages are the ones that refuse a rewrite.
Which file in your repo would you be afraid to open on a shared call with a new engineer?
Answer that, then extract that file first. The rest of the plan is filing. The fear is the map.
The 10,000-line file we split last quarter had 47 useState calls in a single component. I counted them because the compiler would not. After we extracted server components, the remaining client island was 380 lines. That number is why I stopped arguing about “clean code” in the abstract. Size you can count is a better conversation than taste.
If you only do one extraction this week, pull data fetching out of the giant file and onto the server. The UI can stay messy for another sprint. The leak of secrets and the waste of sending database work to the browser cannot.
Frequently asked questions
Should I rewrite the 10,000-line file or extract it piece by piece?
Extract the 10,000-line file piece by piece, starting with the one screen a customer already pays for, because a rewrite gives you a second monolith and no proof the first still works. Keep a Playwright smoke green after every cut. When the named screens are gone, delete what is left instead of translating it.
A weekend rewrite is how the date formatter gets a fifth copy.
When should I add TypeScript in a vibe-coded React app?
Add TypeScript at the seams of a vibe-coded React app: the props that cross a folder and the payloads that cross the network. A overnight conversion of every JSX line stalls the split and tempts the team to paste the monolith back. Type what moves. Leave what will be deleted. Widen the types only when a second engineer has to hold them.
The handbook is there when you need a module, not when you need a feeling of progress.
Does SSR have to come before I split components?
SSR does not have to come before you split components, and it usually should not. Server rendering needs a data function you can lift. A monolith’s useEffect is not that function. Split the view, lift the fetch, then move that fetch to a Server Component. Doing it in the other order copies the tangle into a new runtime.
The 2026 work I still trust is the dull kind. One pile. One test. One smaller file than yesterday.
