> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avocadostudio.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Avocado to your site

> Wire Avocado Studio into a Next.js project you already have — hand one prompt to your coding agent, start two processes, make the first edit.

This page wires Avocado into a Next.js project of your own, using the packages
published on npm. There is no repository to clone. Install them without pinning
a version and let the registry resolve — the twelve packages ship in lockstep,
so a mixed tree is the one thing worth avoiding.

**Nothing here is meant to be typed by hand.** Paste the prompt below into the
coding agent you already have open and let it do the wiring on a branch. The
same work written out — as one shell block, or as seven steps with the reasoning
attached — is on [manual setup](/integration/manual-setup).

<CardGroup cols={2}>
  <Card title="Just looking?" icon="play" href="/first-run">
    **[Try the demo](/first-run)** instead: one command, a nine-page site, no
    API key and no repository. For deciding whether you want this.
  </Card>

  <Card title="Bringing a real site in?" icon="globe" href="/sites">
    This page wires a JSON-backed demo so you see the loop end to end in one
    sitting. **Your** site — real components, a real CMS — is a larger job:
    [bring your site in](/sites).
  </Card>
</CardGroup>

If you would rather understand the model first, start with
[core concepts](/concepts) and [how it works](/how-it-works).

## Prerequisites

* **Node.js 22+** — check with `node --version`
* **A coding agent** — Claude Code, Codex, Cursor, or anything else with a terminal. If you have none, the [shell block](/integration/manual-setup#the-same-thing-as-one-shell-block) does the same work.
* **A Next.js 15 or 16 project on the App Router**, or nothing at all — either path creates one if you point it at an empty directory.
* **One LLM API key** — Anthropic, OpenAI, or Google Gemini. Avocado runs on your key and never resells tokens.

No database, no Docker, no auth setup for local development.

<Warning>
  **Using a Google Gemini key?** Add `@google/genai` too. It is an optional peer dependency of `@avocadostudio-ai/orchestrator-core`, loaded lazily, so no package manager installs it for you and a Gemini plan fails at the first call without it. `ANTHROPIC_API_KEY` and `OPENAI_API_KEY` need nothing extra — both vendor SDKs are ordinary dependencies.
</Warning>

## Hand this to your coding agent

Claude Code, Codex and Cursor already have a terminal in your project. Paste this
and let one of them do the wiring, on a branch, as a diff you review.

This is the *demo* prompt: a JSON file, two built-in blocks, one sitting. The
prompt for bringing a real site in — your components, your CMS, your content — is
a much longer job and lives at [hand it to your coding
agent](/sites/coding-agent).

```text theme={null}
Wire Avocado Studio into this Next.js project (App Router, Next 15 or 16).
Reference: https://docs.avocadostudio.dev/quickstart

Do exactly this, then verify:

0. If this directory has no Next.js app in it yet, create one first:
   npx create-next-app@latest . --ts --app --tailwind --eslint --no-src-dir \
     --import-alias "@/*" --use-npm --yes
   If it already has one, change nothing about it and go to step 1.

1. npm install @avocadostudio-ai/site-sdk @avocadostudio-ai/orchestrator-core

2. next.config.ts — wrap the exported config in `withAvocado` from
   "@avocadostudio-ai/site-sdk/next-config". Do not hand-write
   serverExternalPackages or transpilePackages: withAvocado sets those and the
   server externals together, and setting one of the three alone silently fails
   on the native dependencies.

3. app/globals.css — add `@import "@avocadostudio-ai/blocks/styles.css";` as the
   first line. Without it the page renders correctly and renders unstyled.

4. content/pages.json — one page: { id, slug: "/", title, updatedAt, blocks: [] }
   with a "Hero" block (props: heading, subheading, ctaText, ctaHref) and a "CTA"
   block (props: title, description, ctaText, ctaHref). Both are built-in types.

5. lib/content.ts — getPage(slug), getSlugs(), getPages() reading that JSON,
   plus getSiteConfig() returning a literal like { name: "My Site" }: the file
   holds pages and has nowhere to put site config. Types PageDoc and SiteConfig
   come from "@avocadostudio-ai/site-sdk".

6. app/api/editor/[...path]/route.ts — export { GET, POST, OPTIONS } from
   createEditorApiHandler({ getPages, onPublish, publishSecret }) where
   createEditorApiHandler is from "@avocadostudio-ai/site-sdk/routes",
   onPublish is
   createJsonFilePublishHandler(resolve(process.cwd(), "content/pages.json"))
   from "@avocadostudio-ai/site-sdk/publish-handlers/json-file", and
   publishSecret is process.env.PUBLISH_TOKEN?.trim() || undefined. That route
   overwrites the site's content, so it refuses every request under
   NODE_ENV=production while publishSecret is unset.

7. app/api/avocado/[[...path]]/route.ts — createOrchestrator from
   "@avocadostudio-ai/site-sdk/server" with jsonFileAdapter from
   "@avocadostudio-ai/orchestrator-core/cms" pointed at the same JSON file and
   passed writeOnPublish: true — it defaults to false, which leaves the adapter
   with no onPublish, and Publish then reports success and writes nothing. Plus
   `export const runtime = "nodejs"` and `export const dynamic = "force-dynamic"`.
   Export the handler as GET, POST and OPTIONS — createOrchestrator returns one
   callable, so assign it three times rather than destructuring it.
   Pass a siteId. It must be the same string as step 8's, or the page asks for a
   draft session the orchestrator never seeded.

8. app/[[...slug]]/page.tsx — createSitePage from
   "@avocadostudio-ai/site-sdk/page" with siteId, siteName, getPage, getSlugs,
   getSiteConfig, and siteUrl: process.env.NEXT_PUBLIC_SITE_URL.
   Export default Page AND export { generateStaticParams, generateMetadata } —
   without the second export every page inherits the root layout's title and
   ships no description. Without siteUrl the pages carry no canonical link, no
   og:url, and an og:image left as the relative path the content stores, which
   no social crawler resolves.

9. Delete or move every route that matches a slug now served by getPage,
   app/page.tsx included. A more specific route keeps winning, Next reports no
   conflict and logs nothing, so the site looks unchanged and the integration
   looks dead.

10. .env.local — ANTHROPIC_API_KEY (or OPENAI_API_KEY, or GOOGLE_GENAI_API_KEY),
    ORCHESTRATOR_URL=http://localhost:3000/api/avocado, DRAFT_MODE_SECRET set to
    any long random string, NEXT_PUBLIC_SITE_URL=http://localhost:3000, and
    PUBLISH_TOKEN set to a second long random string. Without DRAFT_MODE_SECRET
    the orchestrator boots and warns that draft mode will not work.

11. .gitignore — add a line for .data/. The orchestrator writes its SQLite state
    there, in the project root, on the first request; create-next-app's
    .gitignore does not cover it, so the first `git add .` after the first run
    commits a database.

Rules: use those import specifiers exactly, never a deep path into dist or src.
Add no middleware and no proxy — mode "auto" needs neither. If this project
already renders pages from a CMS, point getPage/getSlugs at that instead of the
JSON file and tell me you did.

Verify before reporting success. All four must pass:
- `npm run dev` starts with no error
- GET /api/avocado/health returns JSON with "ok": true and "mode": "library"
- GET /api/editor/pages returns the page from content/pages.json
- GET / returns HTML containing the hero heading and `class="hero"`

Do not verify with data-block-id. Those markers render only when the request
asks for the editor (?siteId=&session=, __editor=1, or draft mode), so a plain
GET / correctly has none of them.
```

<Note>
  The prompt is deliberately explicit about the two failures that are silent —
  the shadowing route in step 9 and the missing stylesheet in step 3. Both produce
  a working build and a wrong-looking site, so an agent that reasons from the
  result rather than from the instructions will report success either way.
</Note>

## Start both processes

Two terminals.

<Tabs>
  <Tab title="Your site">
    ```bash theme={null}
    npm run dev
    ```

    Serves your Next.js app on `http://localhost:3000`, with the orchestrator mounted inside it at `/api/avocado`.
  </Tab>

  <Tab title="The editor UI">
    ```bash theme={null}
    npx @avocadostudio-ai/cli start \
      --orchestrator http://localhost:3000/api/avocado \
      --preview      http://localhost:3000
    ```

    Serves the prebuilt editor on `http://localhost:4100`.
  </Tab>
</Tabs>

The CLI's default `--orchestrator` is `http://localhost:4200`, which is the **standalone** server's address and wrong for library mode — pass the flag. On start it probes the orchestrator's `/health` and warns if the reported protocol version does not match the editor build it ships. Every flag it takes: [CLI and packages](/reference/cli).

| Process | URL | What it is |
| - | - | - |
| Your site | `http://localhost:3000` | Your Next.js app, plus the orchestrator at `/api/avocado` |
| Editor | `http://localhost:4100` | The AI chat editor — chat on one side, live preview of your real page on the other |

<Tip>
  To make the site appear in the editor's **Sites** list rather than relying on defaults, run `npx avocado-register --name "My Site" --orchestrator http://localhost:3000/api/avocado` from the project directory. It also generates a `DRAFT_MODE_SECRET` into `.env.local` if you do not have one — which you need in production, and do not need for local `next dev`.
</Tip>

## Make your first edit

Open `http://localhost:4100`. Your page loads in the preview, with an empty chat composer beside it.

Try one of these:

* *"Change the hero headline to 'Cooking made delicious'"*
* *"Add a testimonials section under the hero"*
* *"Rewrite the CTA in a more playful tone"*

What you should see:

1. **A streaming plan** — the planner streams typed operations back, and each one appears in the chat as it is parsed.
2. **A live preview** — operations apply to the real page in the iframe as they arrive.
3. **A change log** — a readable summary of what changed and why.
4. **Keep or revert** — a reply that changed the page ends with **Applied to your draft** and two buttons, **Keep** and **Revert**. Undo and redo also sit above the message box.

Destructive operations — removing a block, deleting a page — are held for explicit approval rather than applied and then undone.

Nothing the chat can do touches a file in your repository. Every edit is one of a fixed set of [typed operations](/concepts) against your content, validated against the block schemas before it is applied.

<Note>
  **Clicking the preview does nothing until you turn the picker on.** The cursor
  button in the composer toggles click-to-select, and it defaults to off so that
  links and carousels keep working while you read. A correct install looks inert
  until you press it. The rest of the interface:
  [a tour of the editor](/editing).
</Note>

## Publish

Publishing is a separate, explicit step, and in library mode it goes through the
adapter. The editor posts to the orchestrator's `/publish`, the orchestrator
takes the current draft for that session, and hands the pages to the adapter's
`onPublish` — which `jsonFileAdapter` only has when you asked for it:

```ts theme={null}
adapter: jsonFileAdapter({
  path: path.join(process.cwd(), "content", "pages.json"),
  writeOnPublish: true,
}),
```

`writeOnPublish` defaults to `false` because on most deployments it cannot work:
Vercel, and every container built the ordinary way, give the running app a
read-only filesystem. It is the right option for a file on your own machine and
the wrong one everywhere else, so a deployed site publishes into something that
persists — a CMS adapter, a database, or a commit back to the repository. The
publish payload is diffed field by field against a baseline, so a target that
writes diffs writes nothing for pages nobody touched. See
[publishing](/integration/publishing).

Everything on this page publishes to your own machine, which is the one place
that needs no credential. **A deployment needs one**, and there are two separate
gates — the orchestrator's, and the site's publish route. Both are on
[security and access](/reference/security).

## What's next

You now have the loop running against a throwaway page. The real question is how your **actual** site gets in, and there are three honest paths.

<CardGroup cols={2}>
  <Card title="Hand it to your coding agent" icon="terminal" href="/sites/coding-agent">
    **The path we recommend.** Claude Code, Codex or Cursor already knows your codebase. Point it at these docs and let it work in your repo through your normal review flow.
  </Card>

  <Card title="Use the built-in onboarding agent" icon="robot" href="/sites/site-agent">
    An agent inside the editor that migrates a public URL or integrates a repository. Early — treat its first pass as a draft. **Needs the standalone orchestrator, so not the library-mode setup you just built.**
  </Card>

  <Card title="Read the contract" icon="book" href="/sites/manual">
    Every seam the integration has to satisfy, whether a human or an agent writes it. This is the reference, not a tutorial.
  </Card>

  <Card title="Prove it worked" icon="clipboard-check" href="/integration/qa">
    Run `npx avocado qa` from the site's directory with the dev server up. It renders every page the way the editor will and exits non-zero until the integration is right. [Coverage checks](/integration/coverage) grade the fields.
  </Card>
</CardGroup>

Then the deeper rabbit holes:

* [Manual setup](/integration/manual-setup) — the same wiring by hand, with the reasoning, plus troubleshooting
* [Custom blocks](/integration/custom-blocks) — register your own React components, which is what a real site edits.
* [Field table](/integration/field-table) — one declaration that drives the schema, the panel, the CMS projection and the merge.
* [CMS adapters](/integration/cms-adapters) — Contentful, Sanity, Strapi, or your own.
* [MCP server](/integration/mcp-server) — 49 tools over Model Context Protocol, for any MCP host.
* [Docker deployment](/operations/docker-deployment) — running the standalone orchestrator instead of library mode.

## Troubleshooting

The full list — including every silent failure mode — is on
[manual setup](/integration/manual-setup#troubleshooting). The three that catch
most people:

**`npm run dev` fails with a `better-sqlite3` or `sharp` error.** Your Next config is not wrapped in `withAvocado`.

**The preview shows the published page and never your edits.** The editor is talking to a different orchestrator than the one inside your site. Confirm you passed `--orchestrator http://localhost:3000/api/avocado`.

**The chat handles only simple, literal edits, and a note above the message box says "AI editing is running without an API key".** No provider key reached the orchestrator, or the dev server has not been restarted since you added one. Keys are read at startup.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.