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.

my-app/ manifest.json # what the agent is, what tabs it has pages/ dashboard/ page.json prompt.seed.md clients/ page.json prompt.seed.md components/ # your custom React components TaxSummary.tsx TaxSummary.schema.ts scripts/ # defaults that the user's AI can edit export.py rules.json CLAUDE.md # agent-wide instructions

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:

  1. A React component. Normal TSX. Use any library you like.
  2. A schema. Declares what props it takes and what data shape it expects.
  3. 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.

The self-reporting contract

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:

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.

The rhythm of building

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.