Skip to content

Workflows with UI

You can easily write a UI to talk to your Llama Deploy served workflows. The framework is unopinionated about your UI stack — bring your own UI. Most templates use Vite with React, but any framework will work that can:

  • build to static assets for production, and
  • read a few environment variables during build/dev
  • In development, the app server starts your UI dev server and proxies it under /deployments/<name>/ui.
  • In production, the Kubernetes operator can serve your built static assets at the same base path.

See the environment variables and configuration details below, and the Deployment Config Reference for UI config fields.

For persisting and querying structured outputs from your UI, see Agent Data Overview and the SDK guides for TypeScript and Python.

Your UI build/dev must read these values to mount under the deployment base path and use the right port:

  • LLAMA_DEPLOY_DEPLOYMENT_URL_ID
  • LLAMA_DEPLOY_DEPLOYMENT_BASE_PATH (e.g., /deployments/<name>/ui)
  • PORT (dev server port)

Pass these through to your client code. For example, Vite can expose them via define, and Next.js can expose them via env/NEXT_PUBLIC_* variables. Libraries like llama-ui read LLAMA_DEPLOY_* variables to configure requests to the workflow backend.

Configure vite.config.ts to read the injected environment and set the base path and port:

vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig(() => {
const basePath = process.env.LLAMA_DEPLOY_DEPLOYMENT_BASE_PATH;
const port = process.env.PORT ? parseInt(process.env.PORT) : undefined;
return {
plugins: [react()],
server: { port, host: true, hmr: { port } },
base: basePath,
// Pass-through env for client usage
define: {
...(basePath && {
"import.meta.env.VITE_LLAMA_DEPLOY_DEPLOYMENT_BASE_PATH": JSON.stringify(basePath),
}),
...(process.env.LLAMA_DEPLOY_DEPLOYMENT_URL_ID && {
"import.meta.env.VITE_LLAMA_DEPLOY_DEPLOYMENT_NAME": JSON.stringify(
process.env.LLAMA_DEPLOY_DEPLOYMENT_URL_ID,
),
}),
},
};
});

Scripts in package.json typically look like:

{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}

Next.js works when exported to static assets. Configure next.config.mjs to use the provided base path, and export static output:

next.config.mjs
const basePath = process.env.LLAMA_DEPLOY_DEPLOYMENT_BASE_PATH || "";
export default {
// Mount app under /deployments/<name>/ui
basePath,
// For assets when hosted behind a path prefix
assetPrefix: basePath || undefined,
// Enable static export for production
output: "export",
// Expose base path to browser for runtime URL construction
env: {
NEXT_PUBLIC_LLAMA_DEPLOY_DEPLOYMENT_BASE_PATH: basePath,
},
};

Ensure your scripts export to a folder (default is out/):

{
"scripts": {
"dev": "next dev",
"build": "next build && next export"
}
}

The dev server will bind to the PORT the app server sets; no extra config needed. For dynamic routes or server features not compatible with static export, you can omit export and rely on proxying to the Python app server, but production static hosting requires a build output directory.

  • Vite: use the configured base or import.meta.env.BASE_URL (or the pass-through var) to prefix asset URLs you build at runtime:
const base = import.meta.env.VITE_LLAMA_DEPLOY_DEPLOYMENT_BASE_PATH || import.meta.env.BASE_URL || "/";
<img src={`${base.replace(/\/$/, "")}/images/logo.png`} />
  • Next.js static export: use the exposed NEXT_PUBLIC_LLAMA_DEPLOY_DEPLOYMENT_BASE_PATH so multi-page routes resolve absolute assets correctly:
const base = process.env.NEXT_PUBLIC_LLAMA_DEPLOY_DEPLOYMENT_BASE_PATH || "";
export default function Logo() {
return <img src={`${base}/images/logo.png`} alt="logo" />;
}

Your UI must output static assets that the platform can locate. Configure ui.directory and ui.build_output_dir as described in the Deployment Config Reference. Defaults to ${ui.directory}/dist.