> For the complete documentation index, see [llms.txt](https://docs.healthuniverse.com/overview/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.healthuniverse.com/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/your-first-health-universe-app.md).

# Your First Health Universe App

This tutorial walks through building a minimal clinical frontend on Health Universe with React and the `@health-universe/react` SDK. By the end you'll have an app that authenticates against the Health Universe API and renders a list of patients.

For a complete, runnable example — provider wiring, a local dev proxy, and a tabbed app with a run-review reader — clone the [`react-starter`](https://github.com/Health-Universe/react-starter) repo as a starting point.

### Step 1: Install the SDK

```bash
npm install @health-universe/react @tanstack/react-query react
```

`react` and `@tanstack/react-query` are peer dependencies, so they live in your app. You do **not** install `@clerk/clerk-react` — the SDK manages Clerk internally (requires `@health-universe/react` ≥ 0.2.2).

### Step 2: How it works with your Health Universe account

You don't build a login or manage sessions. When your app runs on Health Universe, the user is **already signed in to their Health Universe account**, and the SDK authenticates every API request as that user automatically. You just give the provider a way to read the current account, and the platform takes care of the rest.

### Step 3: Set up `HealthUniverseProvider`

Wrap your app once near the root. `HealthUniverseProvider` wires up the authenticated client and a React Query `QueryClient` (pass your own via `queryClient`), and **manages Clerk for you** — it loads Clerk under the hood and authenticates every request as the signed-in user. You never install or import `@clerk/clerk-react`.

```tsx
import { HealthUniverseProvider } from '@health-universe/react'

// API origin WITHOUT the /api/v1 suffix (the SDK adds it). On Health Universe the
// API is served same-origin with your app, so there's no CORS. `??` (not `||`) so
// an explicit '' is respected — '' means same-origin (the dev proxy; see Typical Setup).
const API_URL = import.meta.env.VITE_HU_API_URL ?? 'https://api.healthuniverse.com'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <HealthUniverseProvider
      baseUrl={API_URL}
      // The SDK self-loads Clerk with this key. On Health Universe it's injected
      // automatically; passing it from your env makes the same code work locally too.
      publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}
    >
      {children}
    </HealthUniverseProvider>
  )
}
```

> **On Health Universe you can omit `publishableKey` entirely** — the platform injects the key and the SDK reads it, so `<HealthUniverseProvider baseUrl={API_URL}>` alone is enough and the user is already signed in. Pass it from `VITE_CLERK_PUBLISHABLE_KEY` (a `pk_test_…` key in `.env.local`) so the *same code* also runs in local dev, where there's no injected key.

The provider props:

| Prop                         | Required | Purpose                                                                                                                                               |
| ---------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `baseUrl`                    | Yes      | API origin **without** `/api/v1` (the SDK adds it). From the injected `VITE_HU_API_URL`; an explicit `''` means same-origin (no CORS).                |
| `publishableKey`             | No       | Clerk key the SDK self-loads with. Defaults to the platform-injected key on Health Universe; pass it from `VITE_CLERK_PUBLISHABLE_KEY` for local dev. |
| `organizationId`             | No       | Override the active workspace for org-scoped hooks. Defaults to the signed-in user's active org.                                                      |
| `tokenTemplate`              | No       | Clerk JWT template for the bearer. Defaults to `'app-session'` (carries the `org_id` org-scoped endpoints require).                                   |
| `getToken` / `clerk={false}` | No       | Escape hatch: bring your own token / Clerk instance (e.g. an app already running `@clerk/clerk-react`).                                               |
| `queryClient`                | No       | Your own React Query client. If omitted, the provider creates one.                                                                                    |

> **No `@clerk/clerk-react`.** The SDK bundles and lazy-loads Clerk itself, so sign-in state, the active organization (and its slug), and sign-in/out/org-switching are all available through the SDK — see [`useHealthUniverseAuth`](/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/sdk-reference.md) and the `<SignedIn>` / `<SignedOut>` / `<OrganizationSwitcher>` / `<UserButton>` components. For a quick off-platform smoke test with no Clerk, pass `getToken={async () => import.meta.env.VITE_HU_TOKEN || null}` instead.

### Step 4: Render a patient list

With the provider in place, any component below it can use the SDK's hooks. Here's a minimal searchable patient roster:

```tsx
import { useState } from 'react'
import { usePatients, useHealthUniverse } from '@health-universe/react'

function PatientList() {
  const { organizationId } = useHealthUniverse()
  const [search, setSearch] = useState('')

  const { data, isFetching } = usePatients(
    { organizationId, search: search || undefined, limit: 50 },
    { enabled: !!organizationId },
  )

  return (
    <>
      <input
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Search patients…"
      />
      {isFetching && <p>Loading…</p>}
      {data?.patients.map((p) => (
        <div key={p.id}>
          {p.firstName} {p.lastName} · {p.birthDate}
        </div>
      ))}
    </>
  )
}
```

`useHealthUniverse()` exposes the provider's context (including the active `organizationId`). Org-scoped hooks like `usePatients` take the org id as a query param and accept the standard React Query options as a second argument (`enabled`, `staleTime`, and so on).

### Step 5: Workspaces — one shared, or each visitor's

When you deploy (on the **Privacy & access** step), you choose how the app is bound to a workspace. The SDK handles both modes for you:

* **One shared workspace (pinned)** — the app always works inside the workspace you deployed it into. Everyone who opens it sees that same workspace's data. The SDK activates that workspace automatically; there's nothing to switch.
* **Each visitor's workspace** — the app works inside whoever's using it's own active workspace, just like the main Health Universe app. Different people see their own data, and they can switch between the workspaces they belong to.

You usually don't need to do anything special — `usePatients`, `useRoutines`, etc. already operate in the active workspace. If you want to **let people switch workspaces** (only meaningful in the "each visitor's workspace" mode), render the SDK's `<OrganizationSwitcher>` and gate it on the mode the SDK reports via `useWorkspace()`:

```tsx
import { OrganizationSwitcher, useWorkspace } from '@health-universe/react'

function WorkspaceBar() {
  const { isPinned } = useWorkspace()
  // A pinned app is locked to one workspace — no switcher needed.
  if (isPinned) return null
  return <OrganizationSwitcher />
}
```

The SDK's `<OrganizationSwitcher>` renders Clerk's prebuilt widget (via clerk-js, no `@clerk/clerk-react`). Switching the active workspace updates the SDK's resolved org, and its hooks automatically refetch for the new workspace. Need the active workspace's **slug** (for `useSources` / `useBundleDetail`)? Read it from `useHealthUniverseAuth().organization?.slug`.

### Step 6: Deploy

Push your repo to GitHub, then **Create → Source Code** in Health Universe and select it. The runtime auto-detects as **React** (main file `package.json`), the platform runs your build, and the app is served under a path prefix. See [Typical Project Setup](/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/typical-project-setup.md) for base-path handling and runtime detection, and [Deploying to Health Universe](/overview/building-apps-in-health-universe/deploying-your-app-to-health-universe/deploying-to-health-universe.md) for the full deploy flow.

Congratulations — you've built your first custom clinical frontend on Health Universe. From here, explore the [SDK Reference](/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/sdk-reference.md) to wire up routines, documents, sources/bundles, and chat threads.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.healthuniverse.com/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/your-first-health-universe-app.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
