Building a Houston app.
A practical look at what you actually write when you build a Houston app: the project shape, custom components, and how your own Claude Code can help you.
Start with scaffolding
Create a new project with one command:
pnpm create houston-agent my-app
This gives you a project that runs. It has a manifest, a couple of seed pages, default prompts, and a few common components already wired up.
You don't start with a blank folder. You start with something the user can open and use.
What a Houston app actually is
A Houston app isn't a compiled binary. It's an agent definition — a folder you publish.
Users install your agent into their Houston app. They get the
pages, the prompts, the components, and the scripts — all at
once. Everything under scripts/ becomes the user's
starting point, which the user's AI can customize over time.
Writing a custom component
Houston ships with a base set of components (Table, Card, Form, Chart, Kanban, Timeline, Split, Tabs). Most apps don't need more.
When you do need more, you write a normal React component. Three things make it a Houston component:
- A React component. Normal TSX. Use any library you like.
- A schema. Declares what props it takes and what data shape it expects.
- A description. A one-line label for the AI's index, plus a "when to use this" doc.
You register it with the framework:
import { registerComponent } from "@houston-ai/core"
import { TaxSummary } from "./TaxSummary"
import schema from "./TaxSummary.schema"
registerComponent("TaxSummary", {
component: TaxSummary,
schema,
description: "Annual tax breakdown for a client",
whenToUse: "Use when the user wants to see tax totals by year.",
})
Once it's registered, your component is available to the AI the
same way the base ones are. It shows up in
list_available_components(). It can be referenced by
action chips. It self-reports its structure at render time.
Your component must report its data bindings and the stable IDs of any items it renders. This is how the AI "sees" what the user is looking at. The framework provides a hook for this; chapter 06 explains why the IDs have to come from the data, not from render position.
The build-houston-component skill
Houston ships with a Claude Code skill called
build-houston-component. When you tell your own
Claude Code something like "add a component for rendering a tax
summary," the skill walks Claude through the convention.
Your AI writes the component. The skill makes sure the schema, the description, the "when to use" doc, and the self-reporting are all in place.
This is fractal:
- Your Claude Code builds components at build time.
- Your users' Claude Code composes pages out of them at runtime.
Both follow the same conventions. Both are guided by the same contracts. Neither writes code that ends up in a place it shouldn't.
Shipping and updates
You ship v1 with a set of pages, components, prompts, and scripts. Users install it.
Over time, users teach the app. Their
prompt.local.md files grow. Scripts get edited for
edge cases. Rules accumulate.
When you ship v2, your updated seed prompts, new components, and updated script defaults come along.
The user's local customizations layer on top. Their teachings don't get overwritten. Their script edits stay.
The user gets your updates and keeps what they've built for themselves. Both matter, and the layering protects both.
Ship a small app. Watch users teach it. See what they ask for repeatedly. Those repeated asks become your v2 defaults — learned from real usage, not guessed at the whiteboard.