Build with Veneer
Register the capabilities your app exposes, drop in three components, and let your users redesign the UI with AI — safely sandboxed.
Introduction
Veneer is an open-source React npm package — an “MCP for the UI layer.” Instead of shipping toggles for every layout, you expose a typed surface of data sources and actions; your users describe how they want a page to look, and the model writes real component code. That code runs inside a hard security sandbox and can only ever use what you registered.
Installation
Install the package and add its peer dependencies (react ≥ 18, react-dom ≥ 18).
$ npm install veneer-sdkveneer-sdk package name is a placeholder for this live demo and private preview. The public npm package name and structure may change upon the package’s official release.The package ships untranspiled JSX. With Next.js, add transpilePackages: ['veneer-sdk'] to next.config.js.
Quick start
Declare a registry, pass your real implementations, and render the three components. The registry’s plain-English descriptions are handed to the model; its ids are what the bridge enforces at runtime.
const registry = { dataSources: [{ id: 'tasks', description: "The user's tasks." }], actions: [ { id: 'createTask', description: 'Create a task.' }, { id: 'updateTask', description: 'Update a task.' }, // no deleteTask → generated UIs can never delete ], }; <VeneerProvider registry={registry} actions={actions} data={{ tasks }}> <VeneerFrame style={{ height: 620 }} /> <VeneerChat /> </VeneerProvider>
You also implement the generator endpoint (POST /api/veneer) that calls your LLM and returns { code, reply }.
How it works
- 1You set the rulesList the data and actions your app allows, and plug in the real functions. That list is the only thing the AI can use.
- 2Users describe itThey open the chat and say how they want the page to look. The model writes a self-contained component.
- 3It runs safelyThe component renders in a sandboxed iframe whose only channel is a validated bridge to your registered actions.
The registry
The registry is the contract. A generated UI can only ever invoke an action whose id appears in registry.actions, and read a source whose id appears in registry.dataSources. If there is no deleteTask, deletion is impossible by construction.
Treat each registered action like a public endpoint the user can call with arbitrary arguments — always validate and authorize on the server side inside your implementation.
In-frame API
Generated code is plain JSX with these globals available (no imports):
veneer.data(sourceId)Read a registered data source (returns the current value).veneer.action(id, args)Trigger a registered action (returns a Promise).render(<App />)Mount the component.
fetch, cookies, storage, and any external resource do not work inside the sandbox — all I/O is postMessage through the bridge.
Components & API
VeneerProviderVeneerFrameVeneerChatuseVeneer()VeneerLogoSecurity model
Three independent layers, none of which depend on the model behaving:
- Opaque originsandbox="allow-scripts" with no allow-same-origin — no access to the parent’s cookies, DOM, or storage.
- No networkA strict CSP sets connect-src 'none', so generated code can’t fetch, XHR, open a WebSocket, or send a beacon.
- Validated bridgeThe parent validates every { action, args } message against the registry and runs the real implementation only if the id is registered.
The sandbox is a capability boundary, not a replacement for server-side authorization. Validate arguments and enforce permissions inside every action implementation.
Ready to try it on your own app?