Widget Manifest & UI Testing Guide
Learn how to create and use widget manifests for independent frontend development and comprehensive UI testing in NitroStack Studio.
Overview
The Widget Manifest system revolutionizes frontend development by enabling you to:
- Preview widgets with example data without running the backend
 - Work independently from backend API development
 - Test multiple UI states using different examples
 - Document widget capabilities automatically
 
What is a Widget Manifest?
A Widget Manifest (widget-manifest.json) is a structured JSON file that lives in your project's src/widgets/ directory. It contains:
- Widget metadata (names, descriptions, URIs)
 - Example data for each widget
 - Multiple examples to test different UI states
 - Tags for organization and discovery
 
Quick Start
1. Create Your Widget Manifest
Create src/widgets/widget-manifest.json:
{
  "version": "1.0.0",
  "widgets": [
    {
      "uri": "/user-profile",
      "name": "User Profile",
      "description": "Displays user information and stats",
      "examples": [
        {
          "name": "Active User",
          "description": "User with orders and activity",
          "data": {
            "user": {
              "name": "Emily Johnson",
              "email": "emily@example.com",
              "avatar": "https://api.dicebear.com/7.x/avataaars/svg?seed=Emily"
            },
            "stats": {
              "orders": 12,
              "spent": 1234.56
            }
          }
        }
      ],
      "tags": ["users", "profile"]
    }
  ]
}
2. View in Studio
npm run dev
Navigate to Studio ā Resources Tab ā See your widget in the UI Widgets section!
Complete Manifest Structure
{
  "version": "1.0.0",
  "widgets": [
    {
      "uri": "/widget-route",
      "name": "Widget Display Name",
      "description": "What this widget displays",
      "examples": [
        {
          "name": "Example Scenario Name",
          "description": "Description of this scenario",
          "data": {
            "field1": "value1",
            "field2": "value2",
            "nested": {
              "field3": "value3"
            }
          }
        }
      ],
      "tags": ["category", "type", "feature"]
    }
  ],
  "generatedAt": "2025-01-24T00:00:00.000Z"
}
Field Reference
Widget Fields
| Field | Type | Required | Description | 
|---|---|---|---|
uri | string | ā | Widget route (must match folder name, e.g., /product-card) | 
name | string | ā | Display name shown in Studio | 
description | string | ā | Clear description of widget's purpose | 
examples | array | ā | Array of example data scenarios | 
tags | array | ⬠| Tags for categorization and filtering | 
Example Fields
| Field | Type | Required | Description | 
|---|---|---|---|
name | string | ā | Example name (shown in dropdown selector) | 
description | string | ⬠| Additional context for this example | 
data | object | ā | The actual data props passed to the widget | 
Creating Widget Manifests
Method 1: From Tool Examples
If your tools already have examples, use the response data:
// In your tool definition
@Tool({
  name: 'get_product',
  examples: {
    request: { product_id: 'prod-1' },
    response: {  // ā Use this for widget manifest!
      product: {
        id: 'prod-1',
        name: 'Wireless Headphones',
        price: 79.99,
        image_url: 'https://example.com/headphones.jpg'
      },
      availability: 'In Stock'
    }
  }
})
@Widget('product-card')
async getProduct(input: any) { ... }
Add to manifest:
{
  "uri": "/product-card",
  "name": "Product Card",
  "description": "Displays detailed product information",
  "examples": [
    {
      "name": "In Stock Product",
      "data": {
        "product": {
          "id": "prod-1",
          "name": "Wireless Headphones",
          "price": 79.99,
          "image_url": "https://example.com/headphones.jpg"
        },
        "availability": "In Stock"
      }
    }
  ],
  "tags": ["products", "details"]
}
Method 2: Widget-First Development
Create examples before the backend is ready:
{
  "uri": "/shopping-cart",
  "name": "Shopping Cart",
  "description": "Displays cart items with totals",
  "examples": [
    {
      "name": "Empty Cart",
      "description": "No items in cart",
      "data": {
        "items": [],
        "total": 0,
        "itemCount": 0
      }
    },
    {
      "name": "Cart with Items",
      "description": "2 products in cart",
      "data": {
        "items": [
          {
            "id": "cart-1",
            "product_id": "prod-1",
            "name": "Wireless Headphones",
            "price": 79.99,
            "quantity": 1,
            "image_url": "https://example.com/headphones.jpg"
          },
          {
            "id": "cart-2",
            "product_id": "prod-2",
            "name": "Phone Case",
            "price": 19.99,
            "quantity": 2,
            "image_url": "https://example.com/case.jpg"
          }
        ],
        "total": 119.97,
        "itemCount": 2
      }
    }
  ],
  "tags": ["cart", "ecommerce"]
}
Testing UI States
Create multiple examples to test different scenarios:
{
  "uri": "/order-status",
  "name": "Order Status",
  "description": "Shows current order status",
  "examples": [
    {
      "name": "Pending Order",
      "description": "Order just placed",
      "data": {
        "orderId": "order-123",
        "status": "pending",
        "message": "Your order is being processed",
        "estimatedDelivery": "3-5 business days"
      }
    },
    {
      "name": "Shipped Order",
      "description": "Order in transit",
      "data": {
        "orderId": "order-123",
        "status": "shipped",
        "message": "Your order is on its way!",
        "trackingNumber": "TRK123456789",
        "estimatedDelivery": "Tomorrow"
      }
    },
    {
      "name": "Delivered Order",
      "description": "Order completed",
      "data": {
        "orderId": "order-123",
        "status": "delivered",
        "message": "Your order has been delivered",
        "deliveredAt": "2025-01-24T10:30:00Z"
      }
    },
    {
      "name": "Cancelled Order",
      "description": "Order was cancelled",
      "data": {
        "orderId": "order-123",
        "status": "cancelled",
        "message": "This order was cancelled",
        "reason": "Customer requested cancellation"
      }
    }
  ],
  "tags": ["orders", "status"]
}
Viewing Widgets in Studio
Resources Tab ā UI Widgets
When you navigate to the Resources tab in Studio, you'll see:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā  šØ UI Widgets (16)                      ā
ā                                          ā
ā  āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā  ā
ā  ā šļø Shopping Cart                   ā  ā
ā  ā 2 examples                         ā  ā
ā  āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā⤠ ā
ā  ā Displays cart items with totals    ā  ā
ā  ā /shopping-cart                     ā  ā
ā  ā #cart #ecommerce                   ā  ā
ā  āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā⤠ ā
ā  ā                                    ā  ā
ā  ā  āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā  ā  ā
ā  ā  ā ⨠Cart with Items            ā  ā  ā 
ā  ā  ā                              ā  ā  ā
ā  ā  ā  [Live Widget Preview]       ā  ā  ā
ā  ā  ā  ā¢ Headphones - $79.99       ā  ā  ā
ā  ā  ā  ā¢ Phone Case x2 - $39.98    ā  ā  ā
ā  ā  ā  Total: $119.97              ā  ā  ā
ā  ā  ā                              ā  ā  ā
ā  ā  āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā  ā  ā
ā  ā                                    ā  ā
ā  ā  [Select Example ā¼]  [ā¶ Enlarge]  ā  ā
ā  āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā  ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Interactive Features
- Example Dropdown: Select different examples to see different UI states
 - Live Preview: Widget renders in real-time with selected data
 - Enlarge Button: Open full-screen modal for better viewing
 - Search/Filter: Find widgets by name, description, or tags
 
Best Practices
ā DO: Use Realistic Data
{
  "data": {
    "user": {
      "name": "Emily Johnson",
      "email": "emily.johnson@company.com",
      "avatar": "https://api.dicebear.com/7.x/avataaars/svg?seed=Emily",
      "joinDate": "2024-01-15T00:00:00Z"
    }
  }
}
ā DON'T: Use Placeholder Data
{
  "data": {
    "user": {
      "name": "User Name",
      "email": "user@email.com",
      "avatar": "image.jpg",
      "joinDate": "2024-01-01"
    }
  }
}
ā DO: Match Tool Output Exactly
// Tool returns:
return {
  items: [...],
  total: 123.45,
  itemCount: 3
};
// Manifest should match:
{
  "data": {
    "items": [...],
    "total": 123.45,
    "itemCount": 3
  }
}
ā DO: Provide Multiple Examples
Test edge cases:
- Empty states
 - Single item
 - Multiple items
 - Maximum items
 - Long text
 - Special characters
 
ā DO: Use Descriptive Example Names
Good:
- "In Stock Product"
 - "Low Stock Warning"
 - "Out of Stock"
 
Bad:
- "Example 1"
 - "Test"
 - "Data"
 
Example Workflow
Scenario: Building a Product Listing
Step 1: Create the widget
// src/widgets/app/product-card/page.tsx
'use client';
export default function ProductCard({ data }: { data: any }) {
  const { product, availability } = data;
  
  return (
    <div className="border rounded-lg p-4">
      <img src={product.image_url} alt={product.name} />
      <h3>{product.name}</h3>
      <p className="text-2xl font-bold">${product.price}</p>
      <span className={
        availability === 'In Stock' 
          ? 'text-green-600' 
          : 'text-red-600'
      }>
        {availability}
      </span>
    </div>
  );
}
Step 2: Add to manifest
{
  "uri": "/product-card",
  "name": "Product Card",
  "description": "Displays product with price and availability",
  "examples": [
    {
      "name": "Available Product",
      "data": {
        "product": {
          "name": "Wireless Headphones",
          "price": 79.99,
          "image_url": "https://images.unsplash.com/photo-1505740420928-5e560c06d30e"
        },
        "availability": "In Stock"
      }
    },
    {
      "name": "Out of Stock",
      "data": {
        "product": {
          "name": "Smart Watch",
          "price": 299.99,
          "image_url": "https://images.unsplash.com/photo-1523275335684-37898b6baf30"
        },
        "availability": "Out of Stock"
      }
    }
  ],
  "tags": ["products", "card"]
}
Step 3: Preview in Studio
npm run dev
- Open Studio ā Resources ā UI Widgets
 - Find "Product Card"
 - Toggle between "Available Product" and "Out of Stock"
 - Click Enlarge to view full size
 - Iterate on styles without backend!
 
Troubleshooting
Widgets Not Showing
Problem: UI Widgets section is empty
Solutions:
- Verify 
widget-manifest.jsonexists insrc/widgets/ - Check JSON syntax is valid
 - Rebuild: 
npm run build - Restart: 
npm run dev 
Preview Not Loading
Problem: Widget shows as blank
Solutions:
- Check 
urimatches folder name exactly - Ensure widget component exists at 
src/widgets/app{uri}/page.tsx - Verify 
uristarts with/ - Check browser console for errors
 
Data Not Matching
Problem: Widget shows wrong structure
Solutions:
- Compare manifest 
datawith tool'sexamples.response - Check for typos in field names
 - Verify nested structure matches
 - Ensure data types are correct
 
Advanced: Type-Safe Manifests
Use TypeScript for type safety:
// scripts/generate-manifest.ts
import { defineWidgetMetadata, type WidgetManifest } from 'nitrostack/widgets';
import { writeFileSync } from 'fs';
const manifest: WidgetManifest = {
  version: '1.0.0',
  widgets: [
    defineWidgetMetadata({
      uri: '/product-card',
      name: 'Product Card',
      description: 'Product details display',
      examples: [{
        name: 'Example',
        data: {
          product: { name: 'Item', price: 9.99 },
          availability: 'In Stock'
        }
      }],
      tags: ['products']
    })
  ]
};
writeFileSync(
  'src/widgets/widget-manifest.json',
  JSON.stringify(manifest, null, 2)
);
Run: ts-node scripts/generate-manifest.ts
Next Steps
- Explore Templates: See 
templates/typescript-authfor a complete 16-widget example - Learn Widget Development: Read the Widget Development Guide
 - Build Tools: Learn about Tool Development
 
Questions? Check the main documentation or open an issue on GitHub.