# Visual editing for Astro, with no second source of truth

> Source: <https://dev.to/maninderpreet_singh/visual-editing-for-astro-with-no-second-source-of-truth-1lb7>
> Published: 2026-09-07 14:14:42+00:00

Most visual editors for static sites ask for the same trade. Content moves into a CMS, layout moves into an editor-specific format, and your `.astro` files become a rendering target instead of the thing you edit.

I built [`@sudodevstudio/astro-ai`](https://github.com/SudoDevStudio/astro-ai) to avoid that trade. It is a development-only Astro integration. You select an element on the rendered page, and the edit is written to the source file that produced it.

A separate authoring system is a reasonable choice for some projects. Editorial workflows and non-technical authors need an architecture built for them.

For a site already maintained through code, a second representation adds a standing question: does a manual edit survive the next visual one, and which representation wins when they disagree? I wanted visual edits and manual edits to operate on the same source files, with no separate representation to reconcile. A visual edit is an ordinary source change. It shows up in the same diff and the same pull request as an edit you typed.

That constraint works in both directions. Before the editor offers an operation on the rendered page, it has to establish that the source can express it. A good part of this post is about the operations it declines.

```
npm install --save-dev @sudodevstudio/astro-ai
python
// astro.config.mjs
import { defineConfig } from 'astro/config';
import buildWithAI from '@sudodevstudio/astro-ai';

export default defineConfig({
  integrations: [buildWithAI({ agent: 'codex' })], // or 'claude'
});
```

Start the dev server, open **Build with AI** in Astro's dev toolbar, and select an element. The AI provider is optional. Call `buildWithAI()` with no agent to get deterministic editing alone. For AI requests, authenticate the CLI first with `codex login`, or launch `claude` and complete its login flow.

Editing a heading produces this:

```
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1 +1 @@
-<h1 class="hero-title">Build something useful</h1>
+<h1 class="hero-title">Build something people use</h1>
```

The text changes. The class and the surrounding code stay where they were. Vite HMR renders the updated source.

During development, the integration parses `.astro` files with `@astrojs/compiler-rs` and `.jsx` and `.tsx` files with Babel. Supported elements are instrumented with `data-astro-ai-*` attributes that link a rendered DOM node to a file path and known source ranges.

Deterministic edits replace specific source ranges without regenerating the file from an AST. Formatting and comments outside those ranges remain untouched.

Before an edit is committed, the [command engine](https://github.com/SudoDevStudio/astro-ai/blob/main/src/visual/command-engine.ts) checks that the file still matches the source it inspected, then parses the proposed result. Those two checks reject stale selections and transformations that would introduce a syntax error.

Separating mechanical edits from open-ended ones turned out to matter more than anything else in the design.

Many visual edits have one correct outcome. Changing literal text, setting an existing literal prop, reordering compatible siblings, removing a node, or inserting an element requires no interpretation. The editor knows the source transformation and applies it locally. There is no model request and no token cost on that path.

AI is useful for requests that need reasoning across the implementation. "Make this section responsive" can touch structure, styling, and component boundaries together. "Explain why this renders twice" needs context the DOM does not carry. Those requests go to the Codex or Claude CLI you are already authenticated with. The agent works in a temporary project copy filtered by `.gitignore` and any additional `excludeDirectories`, and you can include project convention files:

```
buildWithAI({
  agent: { provider: 'claude', model: 'your-model' },
  excludeDirectories: ['vendor', 'src/generated'],
  skills: ['AGENTS.md'],
});
```

Credentials stay in the CLI's credential store and are never sent to browser code.

A rendered element is not necessarily an independent unit of source:

``` js
{items.map((item) => <a href={item.url}>Read more</a>)}
```

Every link comes from one template. Editing the literal `Read more` text changes the template, so every link generated from it changes. The URL comes from `item.url`, which is a different source relationship. Selecting one rendered link does not establish whether you meant that data item, the shared template, or the component containing it. Content supplied by an expression or external data has to be edited at its actual source.

Structural edits have comparable boundaries:

`visualComponents`, with the source and target in the same file.
These boundaries reduce the range of operations available. That is the trade I chose over guessing how a visual action should change the underlying program.

Deterministic edits and agent-authored changes pass through the same [patch-transaction store](https://github.com/SudoDevStudio/astro-ai/blob/main/src/visual/patch-transactions.ts). It records file contents before and after a change and exposes readable diffs on a shared undo/redo stack, persisted in `.astro/astro-ai/transactions.json`. That file is local tool state for undo and recovery. The project source still defines the site.

Conflicting changes are reported with a recovery diff rather than overwriting newer source. That path matters because source can change in your code editor while the toolbar is open.

The toolbar, instrumentation, and agent bridge are enabled only in the dev server. The repository includes a [production audit](https://github.com/SudoDevStudio/astro-ai/blob/main/test/verify-production.mjs) that checks build output for known editor markers. The credentialed agent bridge is also disabled when Astro listens beyond loopback, unless you opt in with `allowNetworkAgent: true`.

This suits Astro projects where developers own the source and want to work visually inside the same workflow. It is not a CMS. If you need editorial roles, approvals, and content operations independent of the dev server, those requirements need a different tool. The supported transformations also limit which structures can be edited deterministically today.

I would choose this approach when developers want visual editing while continuing to maintain the site through source files and Git, and when a change made on the page should be reviewable the same way as one made in the editor.

The package targets Astro 7 and Node 22.12+ and is MIT licensed. The [repository](https://github.com/SudoDevStudio/astro-ai) has the installation guide, configuration options, and a demo video.
