System Prompts
Generate and customize prompt instructions from your OpenUI library.
The system prompt tells the LLM how to output valid OpenUI Lang. Generate a serialized library spec at build time, then pass that spec to generateSystemPrompt in your backend.
Where the prompt is built
| Where | Use it when | Code |
|---|---|---|
| CLI | The prompt is the same for every request. | npx @openuidev/cli@latest generate ./src/library.ts --out system-prompt.txt, then send the file's text. |
| Your backend | The prompt needs tools, editMode, inlineMode, or other per-request options. | generateSystemPrompt({ library: librarySpec, promptOptions }) |
| Gateway | Your backend calls models through Gateway. Gateway builds the prompt. | generateSystemPrompt({ cloud: true, library: librarySpec }) |
Gateway accepts only preamble, examples, and additionalRules in promptOptions. It drops every other option: tools, toolExamples, toolCalls, bindings, builtinFunctions, editMode, and inlineMode. For Query, Mutation, or edit mode, build the prompt in your backend.
The sections below cover each path.
1. CLI (recommended for most setups)
openui generate emits two outputs from the same library: a prebuilt prompt and a serialized library spec. Use the spec with generateSystemPrompt; retain the prompt only for static or legacy integrations.
pnpx @openuidev/cli@latest generate ./src/library.tsWrite to a file:
pnpx @openuidev/cli@latest generate ./src/library.ts --out system-prompt.txtThis writes the prompt to system-prompt.txt and the spec to system-prompt.spec.json. The .spec.json file is the input to generateSystemPrompt; --json-schema produces a JSON Schema for other tooling, not the library spec used to build the prompt.
The CLI auto-detects exported PromptOptions (examples, rules) alongside your library. Use --prompt-options <name> to pick a specific export.
2. generateSystemPrompt (recommended backend integration)
For backends that build the prompt per request, with different tools, preambles, or feature flags, use generateSystemPrompt from @openuidev/lang-core. It has no React dependency, so it works in any Node, Edge, or serverless backend. Import the spec emitted by the command above and build the prompt at runtime:
import { generateSystemPrompt, type LibrarySpec } from "@openuidev/lang-core";
import librarySpec from "./generated/system-prompt.spec.json";
const systemPrompt = generateSystemPrompt({
library: librarySpec as LibrarySpec,
promptOptions: {
// Tool descriptions, so the LLM knows what tools exist
tools: myToolSpecs,
// Examples showing how to use your tools with Query/Mutation
toolExamples: [`tickets = Query("list_tickets", {}, {rows: []})\n...`],
// Feature flags
toolCalls: true, // Enable Query(), Mutation(), @Run (default: true if tools provided)
bindings: true, // Enable $variables, @Set, @Reset (default: true if toolCalls)
editMode: true, // Enable incremental editing (LLM outputs patches, not full regen)
inlineMode: true, // Enable text + code responses (LLM can answer questions without code)
// Preamble and extra rules
preamble: "You build dashboards using openui-lang.",
additionalRules: ['Use @Reset after form submit, not @Set($var, "")'],
},
});| Flag | What it enables | Default |
|---|---|---|
toolCalls | Query(), Mutation(), @Run, built-in functions, tool workflow rules | true if tools provided |
bindings | $variables, @Set, @Reset, built-in functions, reactive filters | true if toolCalls is true |
editMode | Incremental editing: the LLM outputs only changed statements | false |
inlineMode | Text and fenced code: the LLM can answer without generating UI | false |
builtinFunctions | Lists the built-in functions (@Count, @Sum, ...) without toolCalls or bindings | true if toolCalls or bindings |
Gateway and Autofix
Pass cloud: true with the generated library specification to emit a configuration block for Gateway or Autofix. Gateway uses it to build the system prompt. Autofix uses it to validate and repair generated OpenUI Lang against your component library. The option name is kept for compatibility.
For Gateway requests, instructions adds extra prose after the configuration block.
import { generateSystemPrompt } from "@openuidev/lang-core";
import librarySpec from "./generated/system-prompt.spec.json";
const instructions = generateSystemPrompt({
cloud: true,
library: librarySpec,
});Gateway accepts preamble, examples, and additionalRules:
const instructions = generateSystemPrompt({
cloud: true,
library: librarySpec,
promptOptions: { preamble: "You build dashboards for Acme." },
});See the Generative UI Quickstart for the Gateway workflow, or the Autofix API to repair output from your existing model integration.
The prompt lists the built-in functions (@Count, @Filter, @Sort, @Each, and the rest) when either toolCalls or bindings is enabled. For static UI libraries without data fetching, they are omitted to keep the prompt concise. Set builtinFunctions: true to include them anyway. A library's own custom functions are always listed.
library.prompt() (frontend shorthand)
If you're generating prompts client-side or in a Next.js route that already imports your library:
import { openuiLibrary, openuiPromptOptions } from "@openuidev/react-ui";
const systemPrompt = openuiLibrary.prompt(openuiPromptOptions);This is convenient but imports React components. Prefer the generated spec plus generateSystemPrompt for backend routes.
What gets generated
The generated prompt includes:
- Syntax rules and expression types
- Component signatures (from your registered components)
- Built-in function reference (
@Count,@Filter,@Sort, etc.), only whentoolCalls,bindings, orbuiltinFunctionsis enabled - Your custom functions, when the library has any
- The Action section with the available actions, including your custom actions, when a component has an action prop or the library has custom actions
- Query/Mutation/Action workflow (if
toolCallsenabled) $variableand reactive binding rules (ifbindingsenabled)- Tool descriptions and tool examples (if
toolsprovided) - Edit mode instructions (if
editModeenabled) - Inline mode instructions (if
inlineModeenabled) - Hoisting/streaming rules
- Your optional examples and rules
Backend usage example
import OpenAI from "openai";
import { generateSystemPrompt, type LibrarySpec } from "@openuidev/lang-core";
import librarySpec from "./generated/system-prompt.spec.json";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const systemPrompt = generateSystemPrompt({
library: librarySpec as LibrarySpec,
promptOptions: { preamble: "You are a helpful assistant." },
});
export async function POST(req: Request) {
const { messages } = await req.json();
const completion = await client.chat.completions.create({
model: "gpt-5.4-mini",
stream: true,
messages: [{ role: "system", content: systemPrompt }, ...messages],
});
return new Response(completion.toReadableStream(), {
headers: { "Content-Type": "text/event-stream" },
});
}