AI app architecture

AI-Generated App Architecture: Pages, Schema, and File Structure from a Prompt

The real guide to AI-generated app architecture — how to produce a scalable Next.js or React project structure, relational schema, and component inventory from a prompt without getting generic scaffolding. With working examples and prompt templates.

Ash Metwalli
April 20, 2026
9 min read
AI app architecturedatabase schemafile structureprompt engineeringAI product planning
AI-Generated App Architecture: Pages, Schema, and File Structure from a Prompt — cover image
Share:

TL;DR

AI tools can generate a complete app architecture — pages, components, relational schema, and file tree — from a single product prompt. The difference between usable architecture and throwaway scaffolding is structure: architectural AI prompts produce reliable output only when they follow a defined order (data model first, then entities, then pages, then shared components, then file tree). This guide gives you the exact ordering, the prompt templates for each layer, and the mistakes that cause AI architecture to fall apart in production. For the category context, read our pillar guide on AI product planning.

Why AI-generated architecture usually breaks

The temptation is to ask the LLM for "a complete architecture" in one prompt. The output looks plausible:

/pages
  /dashboard.js
  /settings.js
/components
  /Sidebar.js
  /ProjectCard.js
/db/schema.sql

Copy that into a fresh repo, try to run it, and three problems show up immediately:

  1. Misaligned imports. ProjectCard.js imports useProjects from /hooks/useProjects.ts — but useProjects was never generated.
  2. Component duplication. The output has <Sidebar /> on the dashboard page and <NavBar /> on the settings page. Both are the same UI element, separately reinvented.
  3. Schema drift. The schema defines a projects table with a status column, but nothing in the UI code ever reads or writes status. It was assumed but never connected.

These are not LLM failures. They are the consequence of asking for architecture before asking for the artefacts architecture should be derived from. Architecture is downstream of stories, features, and schema — not upstream.

The correct ordering

To generate architecture that holds together, run the work in this order:

  1. Schema — the data model comes first because every entity in the app traces to a table
  2. Pages — each page exists because one or more user stories requires it
  3. Shared components — extracted AFTER pages are known, so duplication is explicit
  4. File tree — derived from pages + components, not invented

Inverting this order — starting with the file tree and working backward — is what produces the broken scaffolding above. The file tree is the final projection of decisions made in earlier layers, not a starting point.

Layer 1 — Schema

The schema defines the entities your app manipulates. Until you know the entities, you cannot sensibly design pages or components.

Prompt template:

Given these user stories and features:
<PASTE STORIES + FEATURES>

Generate a relational database schema:
1. Identify every entity mentioned in the stories (including implicit ones like "session", "notification", "invite")
2. For each entity, produce a table with:
   - Column name + Postgres type
   - Nullability
   - Default value (if any)
   - Primary key marker
3. Relationships:
   - Foreign keys with ON DELETE behaviour
   - Junction tables for many-to-many
4. Indexes: add one for every column used in a WHERE, JOIN, or ORDER BY by a user story
5. ENUMs: extract any finite-set status fields into ENUMs
6. Audit trail: if any story mentions history/activity, add created_at/updated_at and a versioning strategy

Output two artefacts:
1. A Mermaid erDiagram block (for visualization)
2. A SQL DDL block (executable against Postgres)

What the model often misses without the explicit rules above:

  • ON DELETE behaviour (cascades become surprises in production)
  • Indexes (without them, the app works in dev and falls over in prod)
  • Audit columns (critical for debugging, skipped if not demanded)

Layer 2 — Pages

Pages exist because stories require them. Generating pages without a story map produces pages that the app doesn't actually need.

Prompt template:

Given these user stories:
<PASTE STORIES>

And this schema:
<PASTE STAGE 1 OUTPUT>

Generate a complete page inventory. For each page:
- Route (e.g. /dashboard, /projects/:id, /settings/billing)
- Purpose (1 sentence)
- Which stories this page satisfies (story names)
- Which schema tables it reads from
- Which schema tables it writes to
- Who can access it (persona roles)
- Required states: empty, loading, error, populated

Rules:
- Every story must be satisfied by at least one page
- No page should exist without at least one story justifying it
- Include auth-required vs public in the access section
- Include admin-only pages as a separate group

Output as JSON array.

Key insight: when you require "which stories this page satisfies", the model cannot hallucinate pages that have no reason to exist. Stories-first architecture eliminates ~30% of the generated scaffolding that would otherwise be dead weight.

Layer 3 — Shared components

The secret to a clean architecture is identifying shared components explicitly. Without explicit identification, the LLM generates the same <DataTable> component five times for five different pages.

Prompt template:

Given these pages:
<PASTE STAGE 2 OUTPUT>

Identify shared components. A shared component is:
- Used by 2 or more pages, AND
- Has identical or near-identical functionality across those pages

For each shared component:
- Name (PascalCase)
- Purpose (1 sentence)
- Which pages use it
- Props it needs (with TypeScript types)
- States it must support (loading, error, empty, populated)
- Whether it accepts children or is a leaf

Rules:
- Aim for 8-15 shared components maximum (not 40 — over-abstraction is worse than duplication)
- Never promote something used by only 1 page
- For each shared component, explain in 1 line why it's shared and not duplicated

Output as JSON array.

The "max 15 shared components" rule is important. AI models over-abstract when asked for a component inventory — they promote every small UI element to shared status. In a medium app, 8–15 truly shared components are realistic; anything beyond that is noise.

Layer 4 — File tree

Now you can generate a file tree. It's derivative of the previous layers.

Prompt template:

Generate a file tree for a Next.js 15 App Router project with these pages and components:
<PASTE LAYER 2 + LAYER 3>

Rules:
- Use App Router conventions (app/[route]/page.tsx)
- Group shared components in /components/<category>/
  where category is one of: ui, forms, layout, data-display, feedback
- Server actions go in /lib/actions/<entity>.ts
- Database types in /database_types.ts (auto-generated; include but mark as generated)
- TypeScript types in /types/<entity>.ts
- Hooks in /lib/hooks/<entity>.ts
- API routes only for things that cannot be server actions (webhooks, public endpoints)

Every file path in the tree must include a 1-line purpose comment.

Output as a tree structure with comments.

Convention-specific rules matter. Without the "App Router" and "server actions over API routes" rules, the model defaults to Pages Router patterns because those dominate the training data.

Shared components vs over-abstraction

The hardest judgment call in AI-generated architecture. Watch for these anti-patterns and reject them:

  • <DataTable> as a shared component. Almost always wrong. A shared table abstraction tries to serve every entity and serves none well. Better: per-entity tables (<ProjectsTable>, <UsersTable>) that share primitive components (<Pagination>, <SortableColumn>).
  • <FormField> as a universal wrapper. Each form has different validation, different error handling, different accessibility requirements. A universal <FormField> becomes a soup of conditional props.
  • <Modal> handling its own open state. Makes shared usage fragile. Prefer controlled modals where the parent owns state.
  • <Layout> doing both site chrome and route logic. Split into <SiteHeader>, <SiteFooter>, <AuthGuard> — each with one job.

The test: would a new engineer joining the project understand when to use the shared component vs when to duplicate? If the rule is non-obvious, the abstraction is wrong.

Schema mistakes AI makes by default

Beyond what the rules catch, these are the mistakes that survive even a well-structured prompt:

  1. ON DELETE CASCADE by default everywhere. Dangerous. Default should usually be SET NULL or RESTRICT; CASCADE is a deliberate choice per relationship.
  2. Passwords stored as TEXT. Should always be varchar(60) (bcrypt hash size) or similar. AI will default to TEXT.
  3. UUIDs as TEXT. Postgres has a uuid type; use it. Same for timestamps — use timestamptz, never TEXT.
  4. Missing updated_at triggers. Adding the column is easy; adding the trigger that updates it on every UPDATE is what the model forgets.
  5. Missing soft-delete columns for entities the spec implies should be recoverable. Add deleted_at timestamptz and filter in queries.
  6. Missing unique constraints. Emails, usernames, invite tokens — all need explicit UNIQUE, not assumed uniqueness.

Always review AI-generated schema against this list before running the DDL.

File-tree mistakes to watch for

  • Every component in a top-level /components flat directory. 50 files in one folder is unmanageable. Group by category.
  • API routes duplicating server actions. In App Router, server actions handle most mutations. API routes are for webhooks, third-party integrations, and public endpoints — not CRUD.
  • /utils/ as a dumping ground. If the file has grown past 5 helpers, split by domain.
  • /types/ duplicated with /database_types.ts. Generated DB types never live in /types/; manual types live only in /types/. Mixing them causes drift.
  • Test files in a separate /tests/ tree instead of co-located. Co-located tests (next to the file under test) scale better and reduce orphaned tests.

The manual pipeline vs an automated one

Running all four layers manually for a medium-complexity product takes 2–3 hours. The risk is each manual paste breaks the chain — miss one persona in the stories, and the schema layer references an entity that no longer exists.

VibeMap runs the same four layers automatically with persistent state. Each layer's output is stored in the project, so when you edit a user story, the schema and pages that reference it are flagged for regeneration. Shared components are extracted at the page stage, not invented. The file tree is generated last, not first.

For a taste without signup, run a single user story through our free User Story Generator — the output is the input for the schema and page generation stages.

Related reading


Try the full architecture pipeline

🎯 Schema + pages + components + file tree — generated consistently, from one prompt.

👉 Try VibeMap free → · Join the Product Hunt launch waitlist →


Sources & further reading

Related Topics

Related Articles

View all posts