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

# Getting Started

> Create, build, and extend your first Studio App

# Getting Started with Studio Apps

This walks through creating a Studio App, building it, and making it show your own data. You need a Datazone project and permission to write to its repository.

## 1. Create the app

Open your project, go to **Studio Apps**, and choose **New studio app**. Give it a name — the alias is derived from it — and pick the branch to create it on.

Datazone commits the whole scaffold to `studio/<alias>/` and adds the entry to `config.yml` in one commit:

```yaml theme={null}
studio_apps:
  - alias: sales_dashboard
    name: Sales Dashboard
    path: studio/sales_dashboard
```

<Note>
  Creating the app does not build it. That is the next step, and it is always explicit.
</Note>

## 2. Build it

Press **Build**. Datazone queues a sandboxed job that runs `npm install` and `vite build`, then publishes the bundle. The first build takes a couple of minutes; later ones are faster.

When the status reaches `READY`, open the app. The scaffold greets you with the signed-in user's email — proof that the session and the API are wired up:

```tsx theme={null}
const user = await getMe()
```

If the build fails, the **Builds** tab has the full log.

## 3. Edit it

You have three ways to change the app, and they all go through the repository:

<CardGroup cols={3}>
  <Card title="In Datazone" icon="code">
    The project's **Code** tab edits files and commits them in place.
  </Card>

  <Card title="With Orion" icon="sparkles">
    Describe the change; Orion writes the files and can design the objects behind them.
  </Card>

  <Card title="Locally" icon="terminal">
    `git clone` the project, edit with your own tools, and push.
  </Card>
</CardGroup>

After any change: **push, then build again.** Datazone flags the served build as stale once the branch has moved on, but it never rebuilds on its own.

## 4. Show your own data

`src/lib/datazone.ts` is the client. Replace the body of `Home` in `src/App.tsx` with a query of your own:

```tsx theme={null}
import { useEffect, useState } from "react"

import { AppLayout } from "@/components/app-layout"
import { executeQuery } from "@/lib/datazone"

type Row = { region: string; total: number }

function Home() {
  const [rows, setRows] = useState<Row[]>([])
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    executeQuery<Row>("select region, sum(amount) as total from sales group by region")
      .then(setRows)
      .catch((problem) => setError(problem.message))
  }, [])

  return (
    <AppLayout title="Sales">
      {error && <p className="text-sm text-destructive">{error}</p>}
      <ul className="divide-y">
        {rows.map((row) => (
          <li key={row.region} className="flex justify-between py-2 text-sm">
            <span>{row.region}</span>
            <span className="font-mono">{row.total}</span>
          </li>
        ))}
      </ul>
    </AppLayout>
  )
}
```

The query runs with the signed-in user's permissions. A dataset they cannot read fails with an error you can show them — which is exactly what the `error` state above is for.

For a query you will use more than once, or one that would otherwise interpolate user input into SQL, publish an [endpoint](/reference/integration/endpoints) and call it with `callEndpoint`.

## 5. Add a page

Create the component, then **add its route** — an imported component with no route is removed from the bundle at build time and its page will 404:

```tsx theme={null}
// src/App.tsx
import { Route, Routes } from "react-router-dom"

import { OrdersPage } from "@/pages/orders"

export default function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/orders" element={<OrdersPage />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  )
}
```

## 6. Add UI components

The scaffold ships a layout shell and leaves `src/components/ui/` empty. Add components with the shadcn CLI, which reads the `components.json` already in your app:

```bash theme={null}
cd studio/sales_dashboard
npx shadcn@latest add button card table
```

The files land in `src/components/ui/` and are yours to edit. Check `package.json` afterwards and pin any version the CLI added as a range — a `^` lets two builds of the same commit install different code.

Style from the theme tokens (`bg-background`, `text-muted-foreground`, `bg-card`, `text-primary`) rather than literal colours, so the app follows Datazone's light and dark themes. The theme lives in `src/index.css`; Tailwind 4 is configured there, not in a `tailwind.config.js`.

## 7. Store data

To let users create or edit records, add a [Knowledge Object](/reference/knowledge-objects/overview) and have the app read and write its instances. Objects are YAML in the same repository, so both ship in the same push:

```yaml theme={null}
# config.yml
objects:
  - path: objects/order.yml
studio_apps:
  - alias: orders
    name: Orders
    path: studio/orders
```

Objects migrate before they can be written to — an app whose objects are not `READY` will load and fail on its first write. Deploy the objects, wait for `READY`, then build the app.

<Warning>
  Do not keep records in `localStorage` or in a file in the repository. `localStorage` is per-browser and lost on the next device, and the bundle is read-only at runtime. Use Knowledge Objects.
</Warning>

## Things that build fine and break in the browser

A Studio App can compile cleanly and still fail once served. These are the causes, in order of how often they happen:

| Symptom                         | Cause                                                                                                          |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Every asset 404s, blank page    | `base` was set in `vite.config.js` — remove it; Datazone passes the correct one                                |
| Every route resolves to nothing | The router's `basename` must stay `import.meta.env.BASE_URL`                                                   |
| A page 404s                     | Its component has no `<Route>` in `App.tsx`                                                                    |
| An image is missing             | Reference `public/` files through `` `${import.meta.env.BASE_URL}logo.svg` ``, not `/logo.svg`                 |
| Every API call is unauthorised  | Use `@/lib/datazone` with relative paths; an absolute URL or an added `Authorization` header drops the session |
| The app reads the wrong data    | A branch-scoped call omitted `branch` and read the default branch                                              |
| A colour renders unstyled       | A custom token needs both a `:root` value and an `@theme inline` entry                                         |

## Working locally

`npm install && npm run dev` renders the app, but API calls will not work: in dev the client resolves the API to `/api`, which the Vite dev server does not serve. Develop layout locally and verify data against a built app, or add your own `/api` proxy to `vite.config.js`.

## Next steps

* [Studio Apps Overview](/reference/studio-apps/overview) — how builds, branches, and serving work
* [Knowledge Objects](/reference/knowledge-objects/overview) — modelling the data behind your app
* [Endpoints](/reference/integration/endpoints) — publishing a query for your app to call
