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 (INITIALCODINGCODE_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 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, 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:

// 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 (INITIALCODINGCODE_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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →