# How the Frontend Manages and Stores Project State in screenshot-to-code

> Discover how the screenshot-to-code frontend manages project state using two Zustand stores for client-side data. Learn about Project Store and App Store for inputs, outputs, and UI lifecycle.

- Repository: [Abi Raja/screenshot-to-code](https://github.com/abi/screenshot-to-code)
- Tags: internals
- Published: 2026-03-02

---

**The screenshot-to-code frontend uses two Zustand stores—Project Store for generation inputs and outputs, and App Store for UI lifecycle state—to keep all project data client-side without server persistence.**

The **screenshot-to-code** repository by abi provides a React-based frontend that converts images into code using AI. To handle complex generation workflows while maintaining privacy, the frontend manages and stores project state entirely within the browser using Zustand-based state management.

## Architecture Overview

### The Two-Store Pattern

The frontend splits state management into two distinct Zustand stores defined in [`frontend/src/store/project-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/project-store.ts) and [`frontend/src/store/app-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/app-store.ts). This separation isolates project data from UI concerns, preventing unnecessary re-renders when interface elements change.

**Project Store** holds the current project’s inputs (images, prompt, assets) and generated outputs (commits, variants, execution consoles). **App Store** tracks UI-level state such as the app’s lifecycle (`INITIAL` → `CODING` → `CODE_READY`), selected elements, and the "select-and-edit" mode.

### Privacy-First Design

Both stores live completely in memory; no project data is ever persisted on the server unless the user explicitly exports it. As implemented in [`frontend/src/components/settings/SettingsTab.tsx`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/components/settings/SettingsTab.tsx), only UI theme preferences touch `localStorage`, while generation inputs and code outputs remain browser-only. This architecture guarantees that all project data stays client-side, enhancing privacy and enabling fast, offline-first interactions.

## Project Store – Core Data Model

The Project Store, defined in [`frontend/src/store/project-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/project-store.ts), uses a rich interface to manage generation workflows. The store is created with `create<ProjectStore>(…)` and provides immutable-by-default updates through pure functions.

### Input State Management

The store captures user inputs through several key fields:

- **`inputMode`**: Tracks the current UI mode as `"image"`, `"video"`, or `"text"`, set via `setInputMode`.
- **`referenceImages`**: An array of data-URL strings representing uploaded screenshots.
- **`initialPrompt`**: The user-typed description that seeds code generation.
- **`assetsById`**: A `Record<string, PromptAsset>` mapping asset IDs to their type and data URL, managed via `upsertPromptAssets` and `resetPromptAssets`.

These fields populate the generation request payload sent to the backend API.

### Output State and Commits

Generated code is organized as a Git-like commit structure:

- **`commits`**: A `Record<string, Commit>` mapping commit hashes to commit objects, each containing one or more **variants** (different model outputs).
- **`head`**: Points to the current "working" commit hash.
- **`latestCommitHash`**: Tracks the most recent commit for quick access.

Core actions include:

- **`addCommit`**: Enriches commits with default variant status (`generating`) and timestamps, then updates `commits` and `latestCommitHash`.
- **`appendCommitCode`**: Concatenates incoming code chunks and records thinking duration.
- **`resizeVariants`**: Aligns the client-side variant array with backend counts while preserving history.
- **`removeCommit`**: Deletes a commit by hash from the store.

### Execution Consoles

For debugging generated code, the store maintains:

- **`executionConsoles`**: A dictionary mapping `variantIndex` to arrays of console output strings.
- **`appendExecutionConsole`**: Adds log lines to a specific variant’s console.
- **`resetExecutionConsoles`**: Clears all console history.

This enables per-variant debugging without cross-contamination between different AI model outputs.

## App Store – UI Lifecycle State

Defined in [`frontend/src/store/app-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/app-store.ts), the App Store manages transient UI state that does not belong to the project data model.

### Application State Transitions

The store tracks the high-level application lifecycle:

- **`appState`**: An enum value (`INITIAL`, `CODING`, `CODE_READY`) representing the current phase.
- **`setAppState`**: Transitions between phases, such as moving from the welcome screen to the generation view via `setAppState(AppState.CODING)`.

### Select-and-Edit Mode

For the interactive editing workflow:

- **`inSelectAndEditMode`**: Boolean flag indicating whether the user is in element selection mode.
- **`toggleInSelectAndEditMode`**: Switches the mode on/off.
- **`disableInSelectAndEditMode`**: Forces the mode off, typically after an edit is complete.

- **`selectedElement`**: Stores the currently highlighted `HTMLElement` for editing.
- **`setSelectedElement`**: Updates the selection when a user clicks a rendered element.
- **`clearSelectedElement`**: Removes the current selection.

### Update Workflow State

When refining existing code:

- **`updateInstruction`**: Stores the user’s text instruction for an update operation.
- **`setUpdateInstruction`**: Updates the instruction text.
- **`updateImages`**: Array of new reference images for the update.
- **`setUpdateImages`**: Replaces the update image set.

## Consuming State in Components

Both stores expose hooks that components import directly from their respective files.

### Reading from Project Store

Components select specific slices to minimize re-renders:

```tsx
// frontend/src/components/unified-input/tabs/UploadTab.tsx
import { useProjectStore } from "../../store/project-store";

const setInputMode = useProjectStore(state => state.setInputMode);

```

```tsx
// frontend/src/components/preview/PreviewPane.tsx
const addCommit = useProjectStore(state => state.addCommit);
...
addCommit(newCommit);

```

Because Zustand returns stable references, components only re-render when the specific slice they subscribe to changes.

### Mutating App State

UI components interact with the App Store for lifecycle and interaction state:

```tsx
// frontend/src/components/sidebar/Sidebar.tsx
import { useAppStore } from "../../store/app-store";

const toggleEdit = useAppStore(s => s.toggleInSelectAndEditMode);
...
<button onClick={toggleEdit}>Select & Edit</button>

```

```tsx
// Setting application phase
const setAppState = useAppStore(s => s.setAppState);
setAppState(AppState.CODING);

```

## Persistence and Data Privacy

The architecture explicitly avoids server-side persistence for project data. As noted in [`frontend/src/components/settings/SettingsTab.tsx`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/components/settings/SettingsTab.tsx), only UI theme preferences and similar settings touch `localStorage`. The README confirms:

> "Only stored in your browser. Never stored on servers."

This design means:
- **All generation inputs** (images, prompts, assets) remain in the Project Store memory.
- **All generated code** (commits, variants) stays client-side unless explicitly exported.
- **No network latency** for state access, enabling instant UI updates.
- **Offline capability** for reviewing previously generated code.

## Summary

- **Dual-store architecture**: The frontend splits state into **Project Store** ([`frontend/src/store/project-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/project-store.ts)) for data and **App Store** ([`frontend/src/store/app-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/app-store.ts)) for UI concerns.
- **Zustand implementation**: Both stores use `create<StoreType>()` with pure setter functions, providing stable hook references that minimize React re-renders.
- **Project data model**: Inputs (images, prompts, assets) and outputs (commits with variants, execution consoles) are organized in a Git-like structure with immutable updates.
- **UI lifecycle management**: App Store tracks generation phases (`INITIAL` → `CODING` → `CODE_READY`) and interactive modes like select-and-edit.
- **Client-side only**: All project state remains in browser memory; no server persistence occurs unless the user explicitly exports data.

## Frequently Asked Questions

### How does screenshot-to-code keep project data private?

The application stores all project inputs and generated code exclusively in browser memory using Zustand stores. According to the source code in [`frontend/src/components/settings/SettingsTab.tsx`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/components/settings/SettingsTab.tsx) and the repository README, data is never transmitted to or stored on remote servers unless the user manually exports it. Only UI theme preferences are saved to `localStorage`.

### What is the difference between Project Store and App Store?

**Project Store** ([`frontend/src/store/project-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/project-store.ts)) manages domain data including uploaded reference images, text prompts, generated code commits, and execution console outputs. **App Store** ([`frontend/src/store/app-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/app-store.ts)) handles transient UI state such as the application lifecycle phase (`INITIAL`, `CODING`, `CODE_READY`), select-and-edit mode flags, and the currently selected DOM element for editing.

### How do components access and update state?

Components import typed hooks directly from the store files, selecting only the specific state slices they need. For example, `const addCommit = useProjectStore(state => state.addCommit)` imports just the commit addition function from [`frontend/src/store/project-store.ts`](https://github.com/abi/screenshot-to-code/blob/main/frontend/src/store/project-store.ts). Zustand returns stable references, ensuring components re-render only when their subscribed slice changes, which optimizes performance across the React tree.

### Can I use screenshot-to-code offline?

Yes, the client-side architecture supports offline operation for reviewing previously generated code. Since all project state resides in memory via the Zustand stores and no server round-trips are required to access commits or variants, users can navigate through their generation history without an active internet connection. However, generating new code requires backend API connectivity.