App Builder
Build custom web applications that run on the Bifrost runtime — standalone React (v2) and the legacy inline model (v1).
What is App Builder?
Section titled “What is App Builder?”App Builder lets you ship custom web applications backed by Bifrost workflows, tables, and auth. An app is a React + TypeScript project that mounts at /apps/{slug} and talks to the platform through the bifrost SDK.
There are two app runtimes. New apps use v2 (standalone_v2).
v2 — standalone_v2 (current default) | v1 — inline_v1 (legacy) | |
|---|---|---|
| What it is | A real, self-contained React project | An inline bundle rendered by the platform |
| Rendering | Owns its own createRoot + <BrowserRouter> | Platform renders your pages inline |
| SDK access | import … from "bifrost" (a real npm package) | Everything injected via globalThis.__bifrost_platform |
| Where deps come from | Normal package.json (Vite, React, shadcn, lucide) | A single "bifrost" import + up to 20 esm.sh packages |
| How it ships | Scaffolded and deployed inside a Solution | Created directly, files edited in place |

v2 — standalone React apps
Section titled “v2 — standalone React apps”A standalone_v2 app is an ordinary React project. It owns its own root and router, and imports the Bifrost SDK as a package served from /api/sdk/download. This is the model new apps default to, and the model Solutions ship.
How it works
Section titled “How it works”bifrost solution scaffold-app <slug> → develop locally → bifrost solution deployScaffold an app inside a Solution workspace, build it like any Vite + React app (bifrost solution start runs the dev server and your workflows behind one origin), then deploy it as part of the Solution. The platform serves the built bundle at /apps/{slug}.
Structure
Section titled “Structure”my-solution/ apps/my-app/ # what `bifrost solution scaffold-app` writes package.json # vite + react + your deps vite.config.ts # Tailwind v4 index.html # loads src/main.tsx src/ main.tsx # createRoot + <BifrostProvider> — keep as scaffolded App.tsx # <BrowserRouter> + <Routes> components/ # your components (incl. shadcn ui/*) pages/ # your route pages functions/hello.py # @workflow functions live at the solution root bifrost.solution.yamlImports — where things come from
Section titled “Imports — where things come from”In a v2 app, only the SDK hooks and providers come from "bifrost". Everything else has its real home:
import { BifrostProvider, BifrostHeader, useWorkflowQuery, useWorkflowMutation, useTable, tables } from "bifrost";import { Button } from "@/components/ui/button"; // shadcn — NOT from "bifrost"import { useState } from "react"; // React — NOT from "bifrost"import { Link, useNavigate } from "react-router-dom"; // router — NOT from "bifrost"import { Phone } from "lucide-react"; // iconsWorkflow hooks
Section titled “Workflow hooks”Run workflows by portable path::function ref (or UUID — bare names aren’t unique):
import { useWorkflowQuery, useWorkflowMutation } from "bifrost";
const { data, isLoading, refetch } = useWorkflowQuery("functions/list_clients.py::main");
const { execute, isLoading: saving } = useWorkflowMutation("functions/save.py::main");await execute({ customer_id: "123" });v1 — legacy inline apps
Section titled “v1 — legacy inline apps”The inline_v1 model predates the standalone runtime. The platform renders your pages inline and injects React, the shadcn component set, react-router, and the workflow hooks through globalThis.__bifrost_platform, so a v1 app imports everything from a single "bifrost" specifier.
A v1 app is a set of files edited in place:
app.yaml # metadata (name, description, esm.sh dependencies)_layout.tsx # root layout — must use <Outlet />, not {children}pages/ index.tsx # / clients/[id].tsx # /clients/:idcomponents/Everything imports from "bifrost":
import { Button, Card, useState, useWorkflowQuery, Outlet, cn, toast } from "bifrost";Two rules that bite v1 authors: use workflow UUIDs (not names), and layouts must use <Outlet /> (not {children}).
Permissions
Section titled “Permissions”Control who can open an app with access_level:
everyone— any signed-in user, including external/portal usersauthenticated— signed-in users except external usersrole_based— specific roles only (setrole_ids)
Inside an app, gate UI on the current user’s roles with useUser() / hasRole() (and RequireRole in v1). See the SDK reference for the exact shapes.
Next Steps
Section titled “Next Steps”- Solutions — how v2 apps are packaged, deployed, and updated
- Run a Solution Locally —
bifrost solution startfor app + workflow dev