Build an embedded app
Build an embedded app
Build an embedded app when a feature belongs inside the Thor dashboard: for example, a product-review screen or a workflow that connects an external service. The React Router template provides the app shell, authentication routes, an authenticated Admin client, and a webhook endpoint.
Before you begin
Use the Thor React Router template and an app registration with a client ID, client secret, app URL, and scopes. Use a development project to install and exercise the app. The registration and runtime URL must refer to the same environment.
1. Configure the template
Create a working copy, then install from its directory:
Set these values from your app registration:
| Variable | Purpose |
|---|---|
THOR_APP_CLIENT_ID | Identifies the registered app |
THOR_APP_CLIENT_SECRET | Server-side app credential |
THOR_APP_URL | URL where the app is reachable |
THOR_APP_SCOPES | Comma-separated scopes matching the app’s active version |
The template example includes offline_access,products:view,channels:view.
Request only the scopes your feature needs and keep the published app version and runtime configuration synchronized.
Do not expose the client secret through a loader or browser bundle.
2. Run in the embedded context
Use a reachable development URL when the dashboard needs to load the app. Keep that URL aligned with the app registration and authentication configuration. Open the installed app from the Thor dashboard so its embedded authentication flow can run. Opening an arbitrary app route outside that context is not a complete installation test.
3. Understand the starting files
| File | Responsibility |
|---|---|
app/thor.server.ts | Server app configuration and exported authentication helpers |
app/root.tsx | App provider, document shell, and dashboard appearance |
app/routes.ts | Route registration |
app/routes/home.tsx | Authenticated product-list example |
app/api/auth.tsx | Authentication entrypoint |
app/api/webhook-handler.tsx | Verified webhook action |
app/types | Generated GraphQL types |
Keep authentication in each protected route’s loader or action.
Do not add another authenticate.admin(request) call to the root loader.
See App authentication and sessions for why route ownership matters.
4. Build one feature
Follow Add a protected app page to query products through the authenticated Admin client. Preserve the template’s app provider, response headers, and error boundary integration. Keep dashboard appearance synchronized through the supplied appearance hook instead of forcing a fixed light theme.
Verify
Run pnpm typecheck and pnpm build.
Test a fresh installation, a returning session, a missing permission, and an expired session.
Before production, configure durable shared session storage and complete the deployment review in App authentication and sessions.