Universal Application Manifest (UAM)
A framework-agnostic JSON specification for defining reactive applications by AI assistants and automated app-generation services. Write once, generate to WildflowerJS, Vue, Solid.js, and more.
What is UAM?
The Universal Application Manifest (UAM) is a framework-agnostic JSON specification for defining reactive applications. An AI assistant reads the UAM manifest alongside a framework-specific idiom file and generates a fully functional application for the target framework.
Framework Agnostic
No framework-specific syntax. The same manifest works for WildflowerJS, Vue, Solid.js, and future targets.
AI-Optimized
Structured JSON format eliminates string formatting issues. Perfect for LLM code generation.
Portable
Compare framework implementations side-by-side. Migrate between frameworks without rewriting logic.
Why UAM?
The Problem
When building the same application for multiple frameworks, developers typically:
- Rewrite the entire codebase for each framework
- Maintain separate repositories with duplicated business logic
- Struggle to keep features in sync across implementations
The Solution
UAM provides a single source of truth:
- One manifest: define your app's components, state, and behavior once
- Framework idiom files: each target framework has an idiom file that teaches the AI its patterns, conventions, and best practices
- AI-generated output: the AI reads both files and produces idiomatic, production-quality code for the target framework
+-------------------+
"Build me a dashboard" ---> | |
| AI Assistant |
UAM Schema (v7) ----------> | |
+---------+---------+
|
+------------------------+
|
v
+-------------------+ +-------------------+
| UAM Manifest |---> | |
| (app.uam.json) | | AI Assistant |---> Framework-specific app
+-------------------+ | |
+-------------------+ +-------------------+
| Framework Idiom |--->
| (.idioms.md) |
+-------------------+
How It Works
Instead of a traditional compiler, UAM uses a manifest + idiom approach driven by an AI assistant:
- Describe the application. The user describes the app they want: its features, behavior, layout, and any specific requirements
- AI drafts the UAM manifest. Using the app description and the UAM schema, the AI assistant creates a structured JSON manifest capturing the app's components, state, stores, templates, styling, and third-party library references
- Choose a target framework. The user, the AI, or a predefined ruleset selects the framework. Each framework has an idiom file (e.g.,
wildflower.idioms.md) documenting its conventions, binding syntax, and best practices - AI builds the application. The AI reads the UAM manifest for what to build and the framework idiom file for how to build it, then generates the complete application
This approach produces higher-quality output than a mechanical compiler because the AI understands framework idioms, can make judgment calls about structure, and generates readable, maintainable code.
UAM Structure
A UAM manifest contains everything needed to generate a complete application:
{
"meta": {
"name": "my-app",
"version": "1.0.0",
"description": "A shopping app with product catalog and cart",
"spec": "Users browse products in a grid, filter by category, add to cart. Cart persists to localStorage."
},
"styles": ".app { padding: 1rem; }",
"stores": {
"cart": {
"state": { "items": { "type": "array", "default": [] } },
"computed": { "total": "return this.state.items.reduce((sum, i) => sum + i.price, 0);" },
"methods": { "addItem": "this.state.items.push(args.item);" }
}
},
"components": {
"app-root": {
"state": { "title": { "type": "string", "default": "My App" } },
"computed": { "cartTotal": "return this.store('cart').computed.total;" },
"template": { /* template tree */ }
},
"product-card": {
"props": { "product": { "type": "object" } },
"methods": { "addToCart": "this.store('cart').addItem({ item: this.props.product });" },
"template": { /* template tree */ }
}
},
"entry": "app-root"
}
Manifest Sections
| Section | Purpose |
|---|---|
meta |
Application metadata: name, version, description, and detailed behavioral spec (meta.spec) |
entry |
Root component name to render |
scripts |
External script URLs to include (CDN libraries, plugins) |
styles |
Global CSS styles for the application |
helpers |
Pure utility functions available in expressions and templates |
integrations |
Third-party library configurations (Chart.js, SortableJS, DataTables, Flatpickr, Driver.js) |
plugins |
UAM plugin references for reusable functionality |
stores |
Global reactive stores with state, computed, methods, persistence, lifecycle, and refs |
components |
UI components with props, state, templates, lifecycle, watchers, and error boundaries |
routing |
URL-based routing with guards, transitions, and query parameter binding |
variables |
Compile-time template variable injection ({{name|"default"}}) |
targets |
Per-framework configuration: idiom file paths, documentation references, and generation hints |
Schema Feature Reference
Detailed documentation of all v7 schema features. For the authoritative schema definition, see uam-schema-v7.json.
Application Metadata (meta)
The meta block identifies the manifest and provides context for AI code generation:
{
"meta": {
"name": "kanban-board",
"version": "1.0.0",
"description": "Kanban board with drag-and-drop and localStorage persistence",
"spec": "Kanban board with drag-drop columns, card priorities, search, and localStorage persistence."
}
}
meta.spec (v7) is the most important field for AI generation. It provides the detailed behavioral specification the AI uses to understand intent beyond the structural data in the manifest. Be thorough: describe features, user interactions, edge cases, and UX details.
Target Framework Configuration (targets)
Links framework-specific idiom guides, documentation references, and generation hints for each compilation target:
{
"targets": {
"wildflower": {
"idioms": "./idioms/wildflower.idioms.md",
"references": ["/llms.txt"],
"hints": { "deepReactivity": true }
},
"vue": {
"idioms": "./idioms/vue.idioms.md",
"references": ["https://vuejs.org/llms.txt"],
"hints": { "deepReactivity": true }
},
"solid": {
"idioms": "./idioms/solid.idioms.md",
"hints": { "deepReactivity": false }
},
"react": {
"idioms": "./idioms/react.idioms.md",
"hints": { "deepReactivity": false }
}
}
}
hints.deepReactivity tells the AI whether the target supports deep reactive proxies (true: direct nested mutations auto-propagate) or requires immutable patterns (false: use spread, produce(), etc.).
Helpers
Pure utility functions available in expressions and templates. Framework-agnostic; the AI generates appropriate patterns for each target:
{
"helpers": {
"formatCurrency": {
"params": ["amount"],
"code": "return '$' + Number(amount).toFixed(2);",
"pure": true,
"description": "Format a number as USD currency"
},
"capitalize": {
"params": ["str"],
"code": "return str.charAt(0).toUpperCase() + str.slice(1);",
"description": "Capitalize first letter of a string"
},
"getTextColor": {
"params": ["bgColor"],
"code": "return helpers.getLuminance(bgColor) > 0.5 ? '#333' : '#fff';",
"description": "Return dark or light text for a given background"
}
}
}
Integrations
Third-party library configurations. The AI generates framework-appropriate initialization, data binding, and cleanup code for each integration type.
Supported Integration Types
| Type | Library | Purpose |
|---|---|---|
sortable |
SortableJS | Drag-and-drop list reordering with cross-list support |
chartjs |
Chart.js | Data visualizations (bar, line, pie, doughnut, radar, scatter, bubble, polarArea) |
datatables |
DataTables | Sortable, searchable, paginated tables with reactive data |
flatpickr |
Flatpickr | Date and time pickers with two-way state binding |
driverjs |
Driver.js | Guided tours and user onboarding |
| (any other key) | Any library | Generic integration via CDN URL, init code, and mount/unmount hooks |
{
"integrations": {
"sortable": {
"lists": [{
"selector": ".card-list",
"group": "kanban",
"handle": ".drag-handle",
"animation": 150,
"itemKey": "card-id",
"containerKey": "column-id",
"onEnd": {
"store": "kanban",
"method": "moveCard",
"params": [
"itemId", "fromContainerId",
"toContainerId", "newIndex"
]
},
"deferInit": "afterPaint"
}]
},
"chartjs": {
"instances": {
"revenueChart": {
"component": "dashboard-widget",
"target": "canvas",
"type": "line",
"data": "store:sales.revenueHistory",
"labels": "store:sales.months",
"reactive": true,
"deferInit": "afterPaint"
}
}
},
"driverjs": {
"tours": {
"welcome": {
"showProgress": true,
"steps": [
{
"element": ".kanban-header",
"title": "Welcome",
"description": "This is your Kanban board"
},
{
"element": ".add-card-btn",
"title": "Add Cards",
"description": "Click here to add a new card"
}
]
}
}
}
}
}
Integration Timing Hints (v7)
All integration instances support two timing hints:
| Property | Values | Purpose |
|---|---|---|
deferInit |
"immediate" | "afterPaint" | "afterMount" |
Controls when the library initializes. Use "afterPaint" when the library needs rendered dimensions (charts, maps). Use "afterMount" when DOM must fully settle. |
scanAfterRender |
true | false |
Set to true if the integration creates DOM containing framework components. WildflowerJS needs wildflower.scan() after such renders. |
Plugins
UAM plugins extend manifest capabilities with reusable functionality. Plugins are resolved from /www/js/uam/plugins/ and can have framework-specific variants:
{
"plugins": [
"toast-notifications",
{
"name": "form-validation",
"variant": "wildflower",
"config": { "showInline": true }
}
]
}
Routing
URL-based routing configuration with guards, view transitions, and query parameter binding:
{
"routing": {
"mode": "hash",
"type": "view",
"stateBinding": "ui.view",
"viewTransitions": true,
"transitionDuration": 300,
"routes": [
{
"path": "/",
"name": "home",
"view": "home"
},
{
"path": "/product/:id",
"name": "product-detail",
"view": "product",
"params": { "id": "ui.selectedProductId" },
"guards": {
"before": "if (!stores.auth.state.isLoggedIn) return '/login';",
"after": "stores.analytics.trackPageView('product', params.id);"
}
},
{
"path": "/search",
"name": "search",
"view": "search",
"queryParams": ["q", "category"],
"queryParamBindings": {
"q": "ui.searchQuery",
"category": "ui.categoryFilter"
}
}
]
}
}
| Property | Purpose |
|---|---|
mode |
"hash" (URL hash, works with static hosting) or "history" (History API, needs server fallback) |
type |
"view" (routes map to state values) or "component" (routes render different components) |
guards.before |
Code run before entering route. Return false to cancel, or a path string to redirect. Has access to from, to, params, query. |
guards.after |
Code run after route loads successfully. Useful for analytics and data fetching. |
queryParams |
Query parameters to sync with store state automatically. |
queryParamBindings |
Map query parameter names to specific state paths (e.g., { "q": "ui.searchQuery" }). |
viewTransitions |
Enable the View Transitions API for smooth page transitions. |
Variables
Compile-time variable injection using {{variableName|"defaultValue"}} syntax. Variables can appear anywhere in the manifest where strings are used:
{
"variables": {
"brandName": {
"description": "Brand name shown in header",
"default": "My App",
"examples": ["My App", "Acme Corp"]
},
"apiBase": {
"description": "Base URL for API calls",
"default": "/api",
"examples": ["/api", "https://api.example.com"]
}
},
"components": {
"header": {
"template": {
"type": "element",
"tag": "h1",
"children": [{ "type": "text", "value": "Welcome to {{brandName|\"My App\"}}" }]
}
}
}
}
Defaults are used when no variable is provided, making manifests self-documenting and functional out of the box.
Store Definition (Full v7)
Stores in v7 have full parity with components, including persistence, watchers, cross-store subscriptions, lifecycle hooks, and non-reactive refs:
{
"stores": {
"kanban": {
"state": {
"columns": { "type": "array", "default": [] },
"nextId": { "type": "number", "default": 1 },
"searchQuery": { "type": "string", "default": "" }
},
"computed": {
"totalCards": {
"code": "return this.state.columns.reduce((s, c) => s + c.cards.length, 0);",
"expensive": true,
"depends": ["columns"]
}
},
"methods": {
"addCard": {
"params": ["columnId", "title", "priority"],
"code": "var col = this.state.columns.find(c => c.id === columnId); col.cards.push({id: this.state.nextId++, title});",
"modifiesState": true
},
"moveCard": {
"params": ["cardId", "fromColId", "toColId", "newIndex"],
"code": "/* move logic */",
"modifiesState": true
}
},
"persistence": {
"storageKey": "kanban-board",
"storage": "local",
"autoSave": true,
"debounce": 500,
"exclude": ["searchQuery"]
},
"watch": {
"searchQuery": "this.filterCards();"
},
"subscribe": {
"settings": ["theme"]
},
"lifecycle": {
"onStoreUpdate": "if (storeName === 'settings') this.applyTheme(newValue);"
},
"destroy": "clearInterval(this.refs.autoSaveTimer);",
"refs": {
"autoSaveTimer": "number",
"sortableInstances": {
"type": "Sortable[]",
"description": "SortableJS instances for cleanup",
"cleanupOnDestroy": true
}
},
"init": "this.loadFromStorage();"
}
}
}
Store Persistence
Automatic state saving and loading via localStorage or sessionStorage:
| Property | Default | Purpose |
|---|---|---|
storageKey |
n/a | Key name for storage (e.g., "kanban-board"). Required to enable persistence. |
storage |
"local" |
"local" for localStorage, "session" for sessionStorage |
autoSave |
true |
Automatically save state on every change |
debounce |
n/a | Debounce save operations by N milliseconds |
include |
n/a | Whitelist: only persist these state paths |
exclude |
n/a | Blacklist: don't persist these state paths |
Non-Reactive Refs
Instance references that persist without triggering reactivity. Use for third-party library instances, DOM references, timers, and other non-reactive values:
{
"refs": {
"chart": "Chart",
"sortable": {
"type": "Sortable",
"description": "SortableJS instance for drag-and-drop",
"cleanupOnDestroy": true
},
"resizeObserver": "ResizeObserver"
}
}
Available on both stores and components. In WildflowerJS, refs map to non-proxied properties on the instance.
Component Definition (Full v7)
Components support lifecycle hooks, watchers, store subscriptions, error boundaries, validation, and refs:
{
"components": {
"kanban-column": {
"props": {
"columnId": { "type": "string", "required": true }
},
"state": {
"isAdding": { "type": "boolean", "default": false },
"newTitle": { "type": "string", "default": "" },
"settingsName": {
"type": "string",
"default": "",
"validation": {
"rules": [
{ "type": "required", "message": "Column name is required" },
{ "type": "maxLength", "value": 30, "message": "Name too long" }
],
"validateOn": "change",
"trim": true
}
}
},
"computed": {
"cardCount": {
"code": "return this.store('kanban').getColumnCards(this.props.columnId).length;",
"uses": ["kanban"]
}
},
"subscribe": {
"kanban": ["columns"]
},
"lifecycle": {
"beforeInit": "this.state.settingsName = this.listItem?.name || '';",
"mount": "this.initSortable();",
"unmount": "if (this.refs.sortable) this.refs.sortable.destroy();",
"onStoreUpdate": "if (storeName === 'kanban') this.refreshCards();"
},
"watch": {
"settingsName": "this.store('kanban').renameColumn(this.props.columnId, newValue);"
},
"refs": {
"sortable": "Sortable"
},
"errorBoundary": {
"onError": "console.error('Column error:', error); return false;",
"fallback": {
"type": "element",
"tag": "div",
"attributes": { "class": "error-state" },
"children": [{ "type": "text", "value": "Something went wrong." }]
}
},
"template": { "type": "element", "tag": "div", "children": [] }
}
}
}
Component Lifecycle Hooks
| Hook | When | Use Case |
|---|---|---|
lifecycle.beforeInit |
Before DOM bindings | Access this.listItem for components inside lists |
init / lifecycle.mount |
After component mounts | Initialize third-party libraries, fetch data |
lifecycle.update |
After reactive updates | Post-update side effects |
destroy / lifecycle.unmount |
Before unmount | Cleanup: destroy library instances, clear timers |
lifecycle.onStoreUpdate |
When subscribed store paths change | React to external state changes. Receives (storeName, changes). |
State Validation
State properties can include validation rules for form fields:
{
"email": {
"type": "string",
"default": "",
"validation": {
"rules": [
{ "type": "required", "message": "Email is required" },
{ "type": "pattern", "value": "email", "message": "Invalid email format" },
{ "type": "maxLength", "value": 254, "message": "Email too long" }
],
"validateOn": "blur",
"trim": true
}
}
}
Supported rule types: required, minLength, maxLength, min, max, pattern (string or preset like "email"), match (field name), custom (code string).
Error Boundaries
Components can define error boundaries to catch and handle errors gracefully, with an optional fallback template:
{
"errorBoundary": {
"onError": "console.error('Component error:', error); return false;",
"fallback": {
"type": "element",
"tag": "div",
"attributes": { "class": "error-fallback" },
"children": [{ "type": "text", "value": "Something went wrong." }]
}
}
}
Code Definition Hints
Methods and computed properties accept generation hints that help the AI produce optimal framework-specific code:
{
"filteredProducts": {
"code": "return this.state.products.filter(p => p.category === this.state.activeCategory);",
"uses": ["products"],
"usesHelpers": ["formatCurrency"],
"expensive": true,
"depends": ["products.all", "ui.activeCategory"]
},
"addToCart": {
"code": "this.store('cart').addItem({ item: args.product });",
"params": ["product"],
"uses": ["cart"],
"modifiesState": true
},
"goToProduct": {
"code": "this.navigate('/product/' + args.id);",
"params": ["id"],
"navigates": true
}
}
| Hint | Applies To | Purpose |
|---|---|---|
expensive |
Computed | Derivation is costly (filter/reduce/sort). AI uses memoization (useMemo, createMemo, Pinia getter). |
depends |
Computed | Explicit dependency list for memoization. Helps AI generate correct dependency arrays. |
params |
Methods | Named parameters. AI uses these instead of generic args objects. |
modifiesState |
Methods | Method mutates store state. AI wraps in set() (Zustand) or produce() (Solid). |
navigates |
Methods | Method involves route navigation. AI injects router/navigate dependencies. |
uses |
Both | Store names this code accesses. AI injects store references via framework patterns. |
usesHelpers |
Both | Helper function names used. AI makes helpers available in scope. |
usesPlugins |
Both | Plugin names accessed. |
usesComponents |
Both | Component names accessed (for cross-component reads). |
Element-Level Transitions
Conditional elements and list items can define CSS transitions for enter/leave animations:
{
"bindings": {
"conditional": {
"when": { "source": "state", "path": "isModalOpen" },
"strategy": "presence"
},
"transition": {
"enter": "fade-in",
"leave": "fade-out",
"duration": 300
}
}
}
List loops also support transitions for item add/remove animations:
{
"loop": {
"source": { "source": "state", "path": "items" },
"as": "item",
"key": "id",
"transition": {
"enter": "slide-in",
"leave": "slide-out",
"duration": 200
},
"template": { "type": "element", "tag": "div", "children": [] }
}
}
Conditional Binding Strategies
UAM distinguishes between two conditional rendering strategies:
| Strategy | Behavior | WildflowerJS | Vue |
|---|---|---|---|
"visibility" |
CSS display toggle. Element stays in DOM, preserves state. | data-show |
v-show |
"presence" |
DOM insertion/removal. Element removed, state lost. | data-render |
v-if |
{
"bindings": {
"conditional": {
"when": { "source": "state", "path": "showPanel" },
"strategy": "visibility"
}
}
}
Template Tree
UAM uses a structured template tree instead of HTML strings:
{
"template": {
"type": "element",
"tag": "div",
"attributes": { "class": "product-card" },
"children": [
{ "type": "text", "value": "Product: " },
{ "type": "binding", "source": "props.name" },
{
"type": "element",
"tag": "button",
"events": { "click": { "handler": "addToCart" } },
"children": [{ "type": "text", "value": "Add to Cart" }]
}
]
}
}
Node Types
| Type | Description | Example |
|---|---|---|
element |
HTML element | { "type": "element", "tag": "div" } |
text |
Static text content | { "type": "text", "value": "Hello" } |
binding |
Dynamic text from state/props/computed | { "type": "binding", "source": "state.count" } |
component |
Child component | { "type": "component", "name": "product-card", "props": {...} } |
conditional |
Conditional rendering | { "type": "conditional", "when": "state.isVisible", ... } |
loop |
List iteration | { "type": "loop", "source": "state.items", "as": "item", ... } |
Universal Patterns
UAM defines standard patterns that work across all frameworks:
Store Access
// Access store from any component
this.store('cart').state.items
this.store('cart').computed.total
this.store('cart').addItem({ item: product })
Event Emission
// Child-to-parent communication
this.emit('item-selected', { id: this.props.item.id })
Two-Way Binding
{
"type": "element",
"tag": "input",
"bindings": {
"model": { "path": "state.email", "type": "text" }
}
}
Framework Idiom Files
Each target framework has an idiom file: a detailed reference document that teaches an AI assistant the framework's conventions, syntax, and best practices. The AI uses the idiom file to translate UAM's abstract intent into idiomatic, production-quality code.
Full idiom coverage. Produces components with data-* attribute bindings, reactive state, and store subscriptions.
wildflower.idioms.md- Full feature parity
Generates Vue 3 SFCs with Composition API and Pinia stores.
vue.idioms.md- Full Pinia integration
Generates Solid.js components with signals and stores.
solid.idioms.md- Fine-grained reactivity
Generates React functional components with hooks.
react.idioms.md- Context API for stores
Demo Applications
See UAM in action with real-world demo applications generated for all four frameworks:
Analytics Dashboard
Charts, metrics, theme switching: demonstrating computed properties and reactive state.
Kanban Board
Drag-and-drop, filtering, card management: demonstrating list operations and actions.
E-commerce Store
Product catalog, cart system, checkout: demonstrating stores and cross-component reactivity.
Getting Started
Using UAM with an AI Assistant
The typical workflow has two phases:
Phase 1: Create the manifest
Describe your application to an AI assistant. The AI uses the UAM schema to produce a structured manifest.
Prompt example:
"Build me an analytics dashboard with live metrics, charts, and a
dark/light theme toggle. Use Chart.js for visualizations."
The AI creates app.uam.json capturing components, stores, state,
computed properties, templates, CSS, and the Chart.js dependency.
Phase 2: Generate the application
Provide the manifest and the target framework's idiom file. The AI generates a complete, runnable application.
Prompt example:
"Here is a UAM manifest [attach app.uam.json].
Using the WildflowerJS idiom file [attach wildflower.idioms.md],
generate a complete WildflowerJS application."
The same manifest can target any supported framework:
# Same manifest, different idiom files:
app.uam.json + wildflower.idioms.md → WildflowerJS app
app.uam.json + vue.idioms.md → Vue 3 + Pinia app
app.uam.json + solid.idioms.md → Solid.js app
app.uam.json + react.idioms.md → React app
Schema Reference
The UAM schema is currently at version 7. The schema is backward-compatible; manifests written for v2–v6 remain valid under v7.
uam-schema-v7.json: current schema version
Best Practices
Do
- Use explicit binding sources (
state.x,props.y) - Define store dependencies in method
usesarrays - Use meaningful component and store names
- Keep methods focused and small
- Use computed properties for derived state
Don't
- Use framework-specific syntax in method code
- Rely on framework-specific lifecycle timing
- Embed complex logic in template bindings
- Use ambiguous binding references
- Forget to specify types for props and state