<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: darren</title>
    <description>The latest articles on DEV Community by darren (@_e85d5d857a773169a2225).</description>
    <link>https://dev.to/_e85d5d857a773169a2225</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3983598%2F78d842ec-2d1c-4b63-a175-317a141ebf19.png</url>
      <title>DEV Community: darren</title>
      <link>https://dev.to/_e85d5d857a773169a2225</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_e85d5d857a773169a2225"/>
    <language>en</language>
    <item>
      <title>Why agent-built apps need a local-first state layer</title>
      <dc:creator>darren</dc:creator>
      <pubDate>Sun, 14 Jun 2026 08:53:05 +0000</pubDate>
      <link>https://dev.to/_e85d5d857a773169a2225/why-agent-built-apps-need-a-local-first-state-layer-2f7a</link>
      <guid>https://dev.to/_e85d5d857a773169a2225/why-agent-built-apps-need-a-local-first-state-layer-2f7a</guid>
      <description>&lt;p&gt;Vibe coding is fast.&lt;/p&gt;

&lt;p&gt;You describe a product idea, ask an agent to build it, and a few minutes later you have a working UI.&lt;/p&gt;

&lt;p&gt;But I kept running into the same problem:&lt;/p&gt;

&lt;p&gt;the first version looks good, but the state architecture breaks quickly.&lt;/p&gt;

&lt;p&gt;Every button starts calling the server directly.&lt;br&gt;
Every component owns a slightly different version of the same data.&lt;br&gt;
Optimistic updates become one-off hacks.&lt;br&gt;
Retries are inconsistent.&lt;br&gt;
Reload recovery is unreliable.&lt;br&gt;
And eventually, every click becomes a server round trip.&lt;/p&gt;

&lt;p&gt;That is why I built VibeLayer.&lt;/p&gt;

&lt;p&gt;VibeLayer is an early open-source local-first state layer for TypeScript apps built by coding agents.&lt;/p&gt;

&lt;p&gt;GitHub:&lt;br&gt;
&lt;a href="https://github.com/ahamoment-101/VibeLayer" rel="noopener noreferrer"&gt;https://github.com/ahamoment-101/VibeLayer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem with agent-generated app state&lt;/p&gt;

&lt;p&gt;When a coding agent builds a UI, the fastest path is usually this:&lt;/p&gt;

&lt;p&gt;async function renameTodo(id: string, title: string) {&lt;br&gt;
  await fetch(&lt;code&gt;/api/todos/${id}&lt;/code&gt;, {&lt;br&gt;
    method: "PATCH",&lt;br&gt;
    body: JSON.stringify({ title }),&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;refetchTodos();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This works in a demo.&lt;/p&gt;

&lt;p&gt;But as the app grows, the pattern spreads everywhere:&lt;/p&gt;

&lt;p&gt;Component -&amp;gt; fetch&lt;br&gt;
Component -&amp;gt; fetch&lt;br&gt;
Component -&amp;gt; fetch&lt;br&gt;
Component -&amp;gt; fetch&lt;/p&gt;

&lt;p&gt;Soon the app has no clear state boundary.&lt;/p&gt;

&lt;p&gt;The UI is tightly coupled to the backend.&lt;br&gt;
Network failures become UI failures.&lt;br&gt;
Local edits are fragile.&lt;br&gt;
The agent has too many places where it can make the wrong architectural decision.&lt;/p&gt;

&lt;p&gt;The issue is not that coding agents are bad.&lt;/p&gt;

&lt;p&gt;The issue is that we often give them no state architecture to follow.&lt;/p&gt;

&lt;p&gt;The rule I wanted&lt;/p&gt;

&lt;p&gt;I wanted a simple rule that both humans and agents could understand:&lt;/p&gt;

&lt;p&gt;UI reads from local state.&lt;br&gt;
UI writes through named mutations.&lt;br&gt;
Mutations are persisted locally first.&lt;br&gt;
Sync happens later through a backend adapter.&lt;/p&gt;

&lt;p&gt;That is the core idea behind VibeLayer.&lt;/p&gt;

&lt;p&gt;Instead of letting components call the backend directly, the app follows a local-first flow:&lt;/p&gt;

&lt;p&gt;User action&lt;br&gt;
  -&amp;gt; named mutation&lt;br&gt;
  -&amp;gt; local state update&lt;br&gt;
  -&amp;gt; durable mutation queue&lt;br&gt;
  -&amp;gt; backend sync&lt;/p&gt;

&lt;p&gt;The user gets an instant UI response.&lt;/p&gt;

&lt;p&gt;The app keeps a durable record of what needs to sync.&lt;/p&gt;

&lt;p&gt;The backend integration is isolated behind a transport adapter.&lt;/p&gt;

&lt;p&gt;And most importantly, the coding agent has fewer choices.&lt;/p&gt;

&lt;p&gt;What VibeLayer does&lt;/p&gt;

&lt;p&gt;VibeLayer gives a TypeScript app three core pieces:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local source of truth&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The UI reads from local state first.&lt;/p&gt;

&lt;p&gt;const todos = client.store.query("todo");&lt;/p&gt;

&lt;p&gt;The app does not need to wait for the server before the interface can respond.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Named mutations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The UI writes business intent instead of calling random APIs.&lt;/p&gt;

&lt;p&gt;await client.mutate("todo.rename", {&lt;br&gt;
  id: "todo_1",&lt;br&gt;
  title: "Ship the demo",&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;The important difference is that the mutation has a name and a contract.&lt;/p&gt;

&lt;p&gt;That makes it easier for both humans and agents to reason about what is allowed to happen.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Durable mutation queue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;VibeLayer persists the local state and the mutation queue before network sync.&lt;/p&gt;

&lt;p&gt;That means the app can survive reloads, temporary network failures, and backend delays.&lt;/p&gt;

&lt;p&gt;The backend catches up later through an adapter.&lt;/p&gt;

&lt;p&gt;Local first.&lt;br&gt;
Persist first.&lt;br&gt;
Sync later.&lt;br&gt;
What VibeLayer is not&lt;/p&gt;

&lt;p&gt;VibeLayer is not trying to replace a full sync platform.&lt;/p&gt;

&lt;p&gt;It is not a database.&lt;br&gt;
It is not a backend service.&lt;br&gt;
It is not a realtime server.&lt;br&gt;
It is not a CRDT editor.&lt;br&gt;
It is not a replacement for every local-first tool.&lt;/p&gt;

&lt;p&gt;The goal is narrower:&lt;/p&gt;

&lt;p&gt;give agent-built apps a safer default state architecture.&lt;/p&gt;

&lt;p&gt;Tools like Replicache, Electric, and PowerSync are serious sync infrastructure.&lt;/p&gt;

&lt;p&gt;VibeLayer is smaller and more opinionated.&lt;/p&gt;

&lt;p&gt;It is a state layer for the messy middle: AI-generated TypeScript apps that need better local state, better mutation boundaries, and fewer direct server calls from UI components.&lt;/p&gt;

&lt;p&gt;Why this matters for coding agents&lt;/p&gt;

&lt;p&gt;Humans can sometimes keep architecture in their heads.&lt;/p&gt;

&lt;p&gt;Agents are different.&lt;/p&gt;

&lt;p&gt;If every component is allowed to call fetch(), the agent will usually choose the fastest local solution.&lt;/p&gt;

&lt;p&gt;That works for one screen.&lt;/p&gt;

&lt;p&gt;It does not work for a real app.&lt;/p&gt;

&lt;p&gt;So the goal is not to make the agent smarter.&lt;/p&gt;

&lt;p&gt;The goal is to give it better boundaries.&lt;/p&gt;

&lt;p&gt;Do not write to the backend from components.&lt;br&gt;
Do not duplicate server state everywhere.&lt;br&gt;
Do not invent a new optimistic update strategy every time.&lt;br&gt;
Use local state.&lt;br&gt;
Use named mutations.&lt;br&gt;
Use the sync adapter.&lt;/p&gt;

&lt;p&gt;This is the kind of rule an agent can follow.&lt;/p&gt;

&lt;p&gt;Example use cases&lt;/p&gt;

&lt;p&gt;I think this pattern is especially useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI-generated SaaS prototypes&lt;/li&gt;
&lt;li&gt;dashboards&lt;/li&gt;
&lt;li&gt;CRUD tools&lt;/li&gt;
&lt;li&gt;internal tools&lt;/li&gt;
&lt;li&gt;admin panels&lt;/li&gt;
&lt;li&gt;offline-tolerant user edits&lt;/li&gt;
&lt;li&gt;apps built with Claude Code, Cursor, Codex, or similar coding agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These apps often do not need full collaborative editing.&lt;/p&gt;

&lt;p&gt;But they do need a cleaner state boundary than scattered fetch() calls.&lt;/p&gt;

&lt;p&gt;Current status&lt;/p&gt;

&lt;p&gt;VibeLayer is early.&lt;/p&gt;

&lt;p&gt;The current repo includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;core runtime&lt;/li&gt;
&lt;li&gt;local store&lt;/li&gt;
&lt;li&gt;named mutations&lt;/li&gt;
&lt;li&gt;durable mutation queue&lt;/li&gt;
&lt;li&gt;IndexedDB storage&lt;/li&gt;
&lt;li&gt;CLI package&lt;/li&gt;
&lt;li&gt;basic todo example&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can try it locally:&lt;/p&gt;

&lt;p&gt;git clone &lt;a href="https://github.com/ahamoment-101/VibeLayer.git" rel="noopener noreferrer"&gt;https://github.com/ahamoment-101/VibeLayer.git&lt;/a&gt;&lt;br&gt;
cd VibeLayer&lt;br&gt;
npm install&lt;br&gt;
npm run example:todo&lt;br&gt;
What I am looking for&lt;/p&gt;

&lt;p&gt;I am not claiming this is production-ready yet.&lt;/p&gt;

&lt;p&gt;I am trying to validate whether this problem feels real to other people building with coding agents.&lt;/p&gt;

&lt;p&gt;The question I care about most is:&lt;/p&gt;

&lt;p&gt;Do agent-built apps need a default local-first state architecture?&lt;/p&gt;

&lt;p&gt;If you have built apps with Claude Code, Cursor, Codex, or other coding agents, I would love blunt feedback.&lt;/p&gt;

&lt;p&gt;GitHub:&lt;br&gt;
&lt;a href="https://github.com/ahamoment-101/VibeLayer" rel="noopener noreferrer"&gt;https://github.com/ahamoment-101/VibeLayer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
