openui-forge-php

A starter guide for building generative user interfaces with a React frontend and a Laravel PHP backend. It shows how to stream OpenAI responses directly to the interface.

In plain words
What is it for?
Use it to start a React and Laravel app that receives OpenAI-generated interface content in real time. It covers setup, API routes, system-prompt generation, and local development.
Why use it?
It removes the guesswork of connecting OpenUI, Laravel, and OpenAI's live response stream. It also states the required software, packages, and environment variable.

Skill for Claude CodeCodex

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add skills/othmanadi/openui-forge/openui-forge-php
Any agent
npx skills add OthmanAdi/openui-forge --skill openui-forge-php
Clone the repo
git clone --depth 1 https://github.com/OthmanAdi/openui-forge

Made for: Claude Code, Codex.

Per session 37 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 2,634 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 1 finding. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.00037 $0.02634
Opus 5 $0.00018 $0.01317
Sonnet 5 $0.00007 $0.00527
Haiku 4.5 $0.00004 $0.00263

Measured 2d ago against content hash f23c1e5eaebd, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

openui-forge-php scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Makes network callslowCapability

Not a fault in itself. Listed so you know the mod talks to something, and to what.

> An official-style OpenAI PHP client exists (`openai-php/client`, requires PHP 8.2+) with a `createStreamed()` helper as an alternative to calling the HTTP endpoint directly. This skill keeps the bundled Guzzle-backed `
.agents/skills/openui-forge-php/SKILL.md · 262 lines

How it starts

The opening of the file, as written. The whole thing — 262 lines — stays where its author put it; the contents beside it link to each section on GitHub.

OpenUI Forge — PHP

Build generative UI apps with a React frontend + Laravel backend. Streams the OpenAI API's native SSE response straight through response()->stream().

Activation Triggers

  • "openui php", "openui laravel", "openui php backend"
  • "generative ui php", "laravel streaming ui backend"

Prerequisites

  • Node.js >= 22 (24 LTS recommended) + React >= 18.3.1 (19+ recommended) (frontend)
  • PHP >= 8.3 + Laravel 13.x (backend; Laravel 13 requires PHP 8.3 minimum and supports 8.3 through 8.5)
  • Composer; guzzlehttp/guzzle ships with Laravel and backs the Http facade (no extra dependency to call OpenAI)
  • OPENAI_API_KEY environment variable set

Quick Start

  1. Create the React frontend and install OpenUI deps:
npm install @openuidev/react-ui @openuidev/react-headless @openuidev/react-lang lucide-react zod
  1. Generate the system prompt:
npx @openuidev/cli generate ./src/lib/library.ts --out backend/storage/app/system-prompt.txt
  1. Create the Laravel backend (see Full Code below). On a fresh app, enable API routes once with php artisan install:api.
  2. Run: php artisan serve on :8000, frontend on :3000

Full Code

Backend: composer.json (require block)

{
    "require": {
        "php": "^8.3",
        "laravel/framework": "^13.0"
    }
}

Laravel bundles guzzlehttp/guzzle, so the Http facade can call OpenAI with no extra package. The OpenAI SSE passthrough below needs nothing beyond the framework.

Backend: routes/api.php

<?php

use App\Http\Controllers\ChatController;
use Illuminate\Support\Facades\Route;

// `php artisan install:api` creates this file and prefixes it with /api,
// so this route is reachable at POST /api/chat.
Route::post('/chat', [ChatController::class, 'chat']);

Backend: app/Http/Controllers/ChatController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ChatController extends Controller
{
    // Loaded once per worker process, then reused across requests.
    private static ?string $systemPrompt = null;

    private function systemPrompt(): string
    {
        if (self::$systemPrompt === null) {
            $path = storage_path('app/system-prompt.txt');
            if (! is_file($path)) {
                abort(500, 'system-prompt.txt not found at ' . $path);
            }
            self::$systemPrompt = (string) file_get_contents($path);
        }

        return self::$systemPrompt;
    }

    public function chat(Request $request): StreamedResponse
    {
        // Read config(), never env(), in a controller: after `php artisan
        // config:cache` (standard in production) env() returns null outside
        // config files. See the config/services.php block below.
        $apiKey = config('services.openai.key');
        if (! $apiKey) {
            abort(500, 'OPENAI_API_KEY not set');
        }

        $incoming = $request->validate([
            'messages'           => 'required|array|min:1',
            'messages.*.role'    => 'required|string',
            'messages.*.content' => 'required|string',
        ])['messages'];

        // Prepend the system prompt; never trust a client-sent system message.
        $messages = array_merge(
            [['role' => 'system', 'content' => $this->systemPrompt()]],
            array_map(
                fn (array $m) => ['role' => $m['role'], 'content' => $m['content']],
                $incoming,
            ),
        );

        $baseUrl = rtrim(config('services.openai.base_url'), '/');
        $model   = config('services.openai.model');

        // stream => true returns the Guzzle PSR-7 response with its body still
        // on the wire, so we read it chunk-by-chunk instead of buffering the
        // whole completion in memory.
        $upstream = Http::withToken($apiKey)
            ->withOptions(['stream' => true])
            ->acceptJson()
            ->post("{$baseUrl}/chat/completions", [
                'model'    => $model,
                'stream'   => true,
                'messages' => $messages,
            ]);

        if ($upstream->failed()) {
            abort($upstream->status(), 'OpenAI request failed: ' . $upstream->body());
        }

        $body = $upstream->toPsrResponse()->getBody();

        return response()->stream(function () use ($body): void {
            // Forward OpenAI's SSE bytes verbatim. OpenAI already emits
            // `data: {chunk}\n\n` frames plus a final `data: [DONE]`, which is
            // exactly what openAIAdapter() parses, so no re-framing is needed.
            while (! $body->eof()) {
                $chunk = $body->read(8192);
                if ($chunk === '') {
                    usleep(1000); // avoid a busy-wait if the stream momentarily has no data
                    continue;
                }
                echo $chunk;
                if (ob_get_level() > 0) {
                    @ob_flush();
                }
                flush();
            }
        }, 200, [
            'Content-Type'      => 'text/event-stream',
            'Cache-Control'     => 'no-cache',
            'Connection'        => 'keep-alive',
            'X-Accel-Buffering' => 'no',
        ]);
    }
}

Read the full file on GitHub · 262 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 2d ago First seen · 262 lines · 37 tokens per session scan A f23c1e5eaebd

Subscribe to this mod's changes

openui-forge-php is a skill published in the GitHub repository OthmanAdi/openui-forge (22 stars, last pushed 1mo ago), licensed MIT. It adds 37 tokens to every session and 2,634 once invoked, about $0.0002 per session on Opus 5. A static security scan graded it A with 1 finding (makes network calls). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.

Related

Other skills, from other repositories

release-notes

Draft concise release notes.

ollama/ollama · 9 tokens

openbot-data-access

Governs how the OpenBot browser app reads and writes server data — every request goes through client in app/src/lib/client.ts, every read is a queryOptions factory in app/src/lib/ /queries.ts, every write is a mutationOptions factory in app/src/lib/ /mutations.ts, and components consume them through…

CopilotKit/OpenBot · 189 tokens

sq-site-dependabot

Reviews, validates, and safely merges Dependabot pull requests for the sq.io site (site/, Bun lockfile). Use when clearing site dependency PRs, triaging Dependabot failures, or checking Lighthouse impact before merge.

neilotoole/sq · 50 tokens

sq

Guides use of the sq CLI to query SQL databases and tabular files with SLQ (sq's jq-like query language) or native SQL, manage sources, choose output formats, and run inspect, diff, and table commands. Use when the user mentions sq, SLQ, wrangling CSV/Excel/JSON/DB data, cross-source joins, or command-line data…

neilotoole/sq · 89 tokens

dd-code-generation

Use pup CLI for immediate Datadog operations or generate code for integration into applications.

DataDog/pup · 16 tokens

drt-analyze

Analyze DRT cluster health for a given time range. Reconstructs the operations timeline, checks CockroachDB metrics (availability, latency, storage, changefeeds, jobs, goroutines, admission control, LSM, KV prober) and logs for anomalies, correlates findings with disruptive operations to distinguish expected…

cockroachdb/cockroach · 149 tokens