Documentation

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).

bash
$ npm install veneer-sdk
⚠️
Preview Notice: The veneer-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.

page.jsx
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

  1. 1
    You set the rules
    List the data and actions your app allows, and plug in the real functions. That list is the only thing the AI can use.
  2. 2
    Users describe it
    They open the chat and say how they want the page to look. The model writes a self-contained component.
  3. 3
    It runs safely
    The 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

VeneerProvider
Holds the registry, the real action implementations, and live data. Persists the current UI (localStorage / sessionStorage / memory via the persist prop).
VeneerFrame
Renders the sandboxed iframe, injects generated code + the in-frame runtime, and runs the parent-side bridge.
VeneerChat
The floating “redesign with AI” chat. Posts to /api/veneer, swaps in the returned UI. Accepts suggestions and endpoint props.
useVeneer()
Access { registry, actions, data, ui, setUi, reset } inside the provider.
VeneerLogo
The product mark, with an optional wordmark.

Security model

Three independent layers, none of which depend on the model behaving:

  • Opaque origin
    sandbox="allow-scripts" with no allow-same-origin — no access to the parent’s cookies, DOM, or storage.
  • No network
    A strict CSP sets connect-src 'none', so generated code can’t fetch, XHR, open a WebSocket, or send a beacon.
  • Validated bridge
    The 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?