Skip to main content
Agent-driven UIs let an AI agent modify the interface at runtime in response to user requests — add a column, swap a chart type, filter a list. Solar’s contract system makes each modification target a known schema, so agents make precise changes without rebuilding everything. The agent knows the component catalog before it writes, and Solar validates every prop before anything reaches the DOM.

How it works

The agent-driven loop looks like this at runtime:
  1. The user describes a change. “Add a delete button to each row.” The request is a natural-language delta, not a full rebuild.
  2. The agent reads registry.manifest(). Before generating code, the agent pulls the full component catalog — every component name, prop type, required flag, and enum. It targets this exact contract instead of inferring conventions from style guides.
  3. The agent generates or modifies the affected component. Because the manifest gives the agent a precise schema, it makes a surgical change — updating one component, not rewriting the whole tree.
  4. The new component is mounted with mountComponent(), replacing the old one. Unmount the previous instance by its id, then mount the new component in the same container. The rest of the UI is untouched.

Mounting and unmounting

mountComponent() returns a numeric instance id. Store that id so you can unmount the component later. unmountComponent() cleans up event listeners, runs effect teardown callbacks, and removes the component’s DOM node.
import { mountComponent, unmountComponent } from './solar/index.js'
import UserCard from './components/UserCard.js'
import UserCardWithActions from './components/UserCardWithActions.js'

// Mount a component; save the id
const instanceId = mountComponent(UserCard, { userId: 42 }, document.getElementById('user-panel'))

// Later — replace it with an updated component:
unmountComponent(instanceId)
const newInstanceId = mountComponent(UserCardWithActions, { userId: 42 }, document.getElementById('user-panel'))
Every container can hold one mounted component at a time. Unmount the current instance before mounting a replacement in the same container.

Why runtime-based matters

Solar has no compile step between generation and execution. The agent generates a component string, you evaluate it or load it as a dynamic module, and the browser runs it immediately. There is no bundler waiting in the loop, no cold-start delay, no hot-reload round trip. This matters for agent-driven UIs because the feedback cycle is everything. The faster the user sees the result of their request, the tighter the interaction loop becomes — and the more useful the agent feels.

The self-correction loop

When the agent makes a prop mistake, Solar throws a ContractError with a machine-readable toJSON() payload. Feed that payload back to the agent as the next prompt and it can correct the mistake and regenerate — without any human intervention.
import { mountComponent, registry } from './solar/index.js'

async function generateAndMount(prompt, container) {
  let attempts = 0

  while (attempts < 3) {
    try {
      const code = await agent.generate(prompt, registry.manifest())
      const component = eval(code)  // or dynamic import
      mountComponent(component, {}, container)
      return
    } catch (err) {
      if (err.name === 'ContractError') {
        // Feed the structured error back to the agent for self-correction
        prompt = `Fix this error and regenerate: ${JSON.stringify(err.toJSON())}`
        attempts++
      } else {
        throw err
      }
    }
  }
}
The ContractError payload includes component, prop, expected, received, and a fix string — everything the agent needs to understand what went wrong and produce a corrected component on the next attempt.

Vibe-coded apps

Solar works well for apps built entirely through prompting — Cursor, Claude, or similar tools writing the full codebase. The usual failure mode in vibe-coded apps is “worked in isolation, broke in context”: the model generates a component that looks correct on its own but clashes with conventions in the rest of the codebase. Solar reduces this failure mode because all component conventions are explicit and machine-readable, not inferred from style guides or example files. The rules are:
  • One component per file
  • File name matches component name
  • Default export is always a defineComponent call
  • Props are always typed and validated
These are rules a model can follow without inference. When the model reads the manifest, it sees the same schema-first pattern in every entry — defineComponent calls all the way down. There is nothing to infer and nothing to guess.
Because Solar is runtime-based with no build step, the generation-to-render loop is much tighter than with compiler-based frameworks. Compiler-based frameworks require a full rebuild before the browser can run new code. With Solar, the agent generates a component and the browser executes it in the same tick — which makes real-time agent-driven UI modification practical rather than theoretical.