Starter FreeUpdated April 27, 2026

Project Structure

Understand how Starter Free is organized so you can extend it without breaking its architecture.

StarterStructure

OverviewLink to section

Starter Free is structured for clarity and long-term evolution.

This is not a demo folder. It is a production-shaped architecture designed to help you extend features without turning pages into large, fragile files.

Understanding the structure early helps you:

  • extend features cleanly
  • keep routes readable
  • wire backend progressively
  • avoid refactors caused by mixed responsibilities

Key idea

You are not only learning where files live. You are learning how product architecture scales from a mocked SaaS surface to a real product.

Mental modelLink to section

Pages define routes. Components define features. UI defines primitives.

LayerResponsibility
app/Routing, layouts, and product entry points
components/Feature composition and interaction logic
@pycolors/uiStateless UI primitives used across the product

System rule

Keep pages thin. When interaction logic grows, move it into a feature folder.

StructureLink to section

layout.tsx
page.tsx
layout.tsx
project-table.tsx
project-row-actions.tsx
project-rename-dialog.tsx
project-confirm-delete-dialog.tsx
project-types.ts

This structure is intentionally small. It separates route ownership from feature composition.

Architecture flowLink to section

architecture-flow.txt
App Router pages

Feature components

PyColors UI primitives

A page should describe the product surface. Feature components should own interactions. UI primitives should stay reusable and stateless.

Route layer

The route layer defines URLs, page structure, layout composition, and high-level orchestration.

Page structureLayoutsNavigationRoute composition

App directoryLink to section

The app/ directory is the routing layer.

It defines:

  • auth entry points
  • dashboard pages
  • product routes
  • settings and billing surfaces
  • high-level layout composition

Page rule

A page should compose the feature. It should not become the feature itself.

Components directoryLink to section

The components/ directory contains feature-level building blocks.

For example, the projects feature keeps table rendering, row actions, dialogs, and mock types together.

project-table.tsx
project-row-actions.tsx
project-rename-dialog.tsx
project-confirm-delete-dialog.tsx
project-types.ts

This keeps the route clean:

app/projects/page.tsx
import { ProjectTable } from "@/components/projects/project-table";

export default function ProjectsPage() {
  return <ProjectTable />;
}

And moves feature behavior into the feature folder.

components/projects/project-row-actions.tsx
export function ProjectRowActions() {
  return (
    // dropdown actions, rename dialog, delete confirmation
    // and later API wiring live here
    null
  );
}

Mock data strategyLink to section

Mock data lives close to the feature using it.

project-types.ts

components/projects/project-types.ts
export type Project = {
  id: string;
  name: string;
  status: "active" | "draft" | "archived";
};

export const MOCK_PROJECTS: Project[] = [
  {
    id: "project_1",
    name: "Acme Dashboard",
    status: "active",
  },
];

Later, you replace the mock source with:

  • API calls
  • server actions
  • database queries
  • authenticated user data

The UI structure can remain stable.

Why this is powerful

Mock data is temporary. Product structure is not. Keep the structure stable and replace the source of truth when the product is ready.

PageShell patternLink to section

Main SaaS pages use a shared shell for consistent page structure.

components/layout/page-shell.tsx
<PageShell
  title="Projects"
  description="Manage your product entities."
  actions={...}
  meta={...}
/>

Use this pattern to keep:

  • page spacing consistent
  • headers predictable
  • actions aligned
  • metadata structured

Do not bypass it unless the page truly needs a different layout.

Dialog patternLink to section

Dialogs are isolated from the page and controlled by their parent feature.

project-rename-dialog.tsx
project-confirm-delete-dialog.tsx

Recommended ownership:

ConcernOwner
Open stateParent feature component
Form UIDialog component
API call laterFeature/service layer
Visual primitive@pycolors/ui Dialog

This makes rename and delete flows easier to wire later without changing page structure.

Add a new featureLink to section

Use the existing Projects pattern as your template.

Add the routeLink to section

terminal
mkdir -p app/reports
touch app/reports/page.tsx

Create the feature folderLink to section

terminal
mkdir -p components/reports
touch components/reports/report-table.tsx
touch components/reports/report-row-actions.tsx
touch components/reports/report-types.ts

Compose the pageLink to section

app/reports/page.tsx
import { ReportTable } from "@/components/reports/report-table";

export default function ReportsPage() {
  return <ReportTable />;
}

Decision guideLink to section

Use the Starter Free structure if:

  • you want thin route files
  • features need to evolve independently
  • mock data will later become backend data
  • dialogs and row actions need to stay reusable
  • you want a clear path from frontend surface to production wiring

Avoid changing the structure if:

  • you have not explored the full vertical slice yet
  • the change only saves a few lines today
  • the route would start owning too much logic
  • UI primitives would start fetching data
  • the mock layer would become scattered globally

Prefer

  • feature-based folders
  • thin pages
  • mock data close to the feature
  • isolated dialogs and row actions
  • UI primitives that stay stateless

Avoid

  • putting feature logic directly in pages
  • fetching data inside UI primitives
  • placing all mock data in one global file
  • duplicating dialog logic across routes
  • deleting structure before understanding it

Backend wiring laterLink to section

When you connect a backend, the structure stays familiar.

TodayLater
MOCK_PROJECTSdatabase-backed projects
local dialog stateserver action or API mutation
disabled billing portalStripe billing portal
mocked auth screensreal auth provider
static membersorganization members from backend

The goal is not to rewrite the UI. The goal is to replace the data source.

Upgrade when wiring becomes the blocker

Starter Free validates the product surface. Starter Pro wires the business layer with production-shaped auth, billing, backend, and delivery foundations. → /docs/starter-pro

Common questionsLink to section

Next stepsLink to section