How the Frontend Manages and Stores Project State in screenshot-to-code
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 and 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, 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, 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 viasetInputMode.referenceImages: An array of data-URL strings representing uploaded screenshots.initialPrompt: The user-typed description that seeds code generation.assetsById: ARecord<string, PromptAsset>mapping asset IDs to their type and data URL, managed viaupsertPromptAssetsandresetPromptAssets.
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: ARecord<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 updatescommitsandlatestCommitHash.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 mappingvariantIndexto 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, 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 viasetAppState(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 highlightedHTMLElementfor 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:
// frontend/src/components/unified-input/tabs/UploadTab.tsx
import { useProjectStore } from "../../store/project-store";
const setInputMode = useProjectStore(state => state.setInputMode);
// 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:
// frontend/src/components/sidebar/Sidebar.tsx
import { useAppStore } from "../../store/app-store";
const toggleEdit = useAppStore(s => s.toggleInSelectAndEditMode);
...
<button onClick={toggleEdit}>Select & Edit</button>
// 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, 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) for data and App Store (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 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) manages domain data including uploaded reference images, text prompts, generated code commits, and execution console outputs. App Store (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. 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →