Where design editor behavior lives and how to navigate DesignEditor.tsx. Use before changing any editor behavior — undo/redo, paste, delete, duplicate, style commit, layer move/rename, export, structure change — or before opening `app/pages/DesignEditor.tsx`.
Scanned 9/3/2026
Install to Claude Code
npx -y skills add BuilderIO/agent-native --skill design-editor-architecture --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Design Editor Architecture?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/builderio-design-editor-architecture)More formats (shields.io, HTML) on the badges page.
---
name: design-editor-architecture
description: >-
Where design editor behavior lives and how to navigate DesignEditor.tsx. Use
before changing any editor behavior — undo/redo, paste, delete, duplicate,
style commit, layer move/rename, export, structure change — or before opening
`app/pages/DesignEditor.tsx`.
scope: dev
metadata:
internal: true
---
# Design Editor Architecture
## The routing rule
Editor **behavior** lives in `app/pages/design-editor/`, not in
`app/pages/DesignEditor.tsx`.
`DesignEditor.tsx` is ~20,900 lines and holds only three things: state
declarations, the `useCallback` wrappers that gather arguments, and the JSX.
Every wrapper delegates to a `run<Name>()` command module.
**To change what an editor action does, edit the command module.** Opening
`DesignEditor.tsx` to change behavior is almost always the wrong move — it will
exhaust your context before you find the code.
| Directory | Holds |
| --- | --- |
| `design-editor/commands/` | 86 one-per-action modules, each exporting `run<Name>(args, …)`. Start at `commands/README.md` |
| `design-editor/effects/` | Subscription and autosave loops (collab text, motion autosave, agent selection mirroring) |
| `design-editor/derive/` | Pure derivations (`overview-screens.ts`, `design-breakpoints.ts`) |
| `design-editor/domains/` | Whole-domain hooks owning state + refs + effects + handlers together (`use-tweaks.ts`) |
| `design-editor/*.ts` | Shared helpers: `history.ts`, `selection-state.ts`, `pending-edits.ts`, `editor-state.ts`, `editor-helpers.ts`, … |
## Common task → file
| Task | File |
| --- | --- |
| Undo / redo | `commands/undo.ts`, `commands/redo.ts` |
| Paste | `commands/editor-paste.ts` routes; `commands/paste-selection.ts` and `commands/paste-over-selection.ts` do layers |
| Copy | `commands/copy-selection.ts` |
| Delete | `commands/delete-selection.ts` (layers), `commands/delete-files.ts` (screens) |
| Duplicate | `commands/duplicate-selection.ts`, `commands/duplicate-screen.ts` |
| Style commit | `commands/commit-visual-styles.ts` |
| Inspector style edit | `commands/style-change.ts` (one property), `commands/styles-change.ts` (many) |
| Structure change | `commands/visual-structure-change.ts`, `commands/screen-visual-structure-change.ts` |
| Layer move / rename | `commands/layer-move.ts`, `commands/layer-move-to-screen.ts`, `commands/layer-rename.ts` |
| Lock / hide a layer | `commands/toggle-layer-locked.ts`, `commands/toggle-layer-hidden.ts` |
| Export | `commands/render-png-blob.ts`, `download-pdf.ts`, `download-svg.ts`, `copy-as-figma-svg.ts` |
| Save / persistence | `commands/save-file-content.ts`, `commands/apply-file-content-update.ts` |
| Breakpoints | `responsive-breakpoints` skill; `derive/design-breakpoints.ts` |
A `screen-` prefix means the command is addressed by an explicit `screenId`
(overview canvas or board). The unprefixed twin acts on the focused screen.
Before adding a `domains/` hook, count what it would expose. Past roughly 16
returned values the hook stops hiding anything and just relocates the wiring —
measured surfaces for share/export (20), generation (18), and motion (48) are
all why those still live inline. Also check no input it needs is declared after
the point where its own outputs are first consumed; responsive-interact fails
that test and cannot be extracted without changing when values are read.
## Navigating DesignEditor.tsx when you must
The file carries ~82 section banners. This prints a table of contents:
```bash
grep -n "──" app/pages/DesignEditor.tsx
```
Read one region with an offset and a limit instead of opening the file. Every
section is under ~800 lines. Banners use `// ── Name ──` in the component body
and `{/* ── Render: name ── */}` inside the JSX.
When you add or move a region, add a banner for it.
## Hard constraints
**`DesignEditor.tsx` must keep exactly one runtime export — the default — and
zero runtime named exports.** Type-only exports are fine. Both routes
(`app/routes/design.$id.tsx`, `app/routes/visual-edit.$id.tsx`) import that
default. A named export breaks React Fast Refresh for the whole editor.
**The render-callback trio's dependency arrays are load-bearing.**
`DesignEditor.routeRefreshBoundary.test.ts` parses the file with the TypeScript
compiler and enforces:
- `renderScreenContent` deps are exactly `[renderEditableScreenContent]`
- `renderBreakpointContent` deps are exactly `[renderEditableScreenContent]`
- `renderEditableScreenContent` deps include `activeBreakpointWidthState`,
`motionDefaultEase`, `motionDurationMs`, `inScreenGradientEditTarget`,
`handleInScreenGradientEditChange`, and `statePreviewTarget`
That last one exists because cached overview canvases must invalidate on
preview-only state changes. Dropping a name from it renders a stale canvas.
**Many specs read source as text.** ~20 specs `readFileSync` either
`DesignEditor.tsx` or a command module and slice it with `indexOf` markers, then
assert on the source string. Moving code breaks them with a confusing failure.
Re-point the path and the marker in the same commit that moves the code. Prefer
asserting against the command module (`commandSource("undo.ts")`) over the
editor file.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!