Creating ChatGPT Widgets
Create production-ready widgets for ChatGPT Apps following official OpenAI guidelines.
What This Skill Does
- Creates widgets for ChatGPT Apps with
window.openai integration
- Supports all display modes (inline, fullscreen, pip)
- Implements theme support, accessibility, responsive design
- Follows official OpenAI UX/UI guidelines
What This Skill Does NOT Do
- Create native mobile apps or browser extensions
- Build MCP server backend logic
- Handle user authentication (see
references/authentication.md for patterns)
- Deploy widgets to production
- Create standalone web applications
Dependencies
| Dependency |
Version/Notes |
window.openai API |
Required - injected by ChatGPT runtime |
| Modern browser |
ES2020+, CSS Grid/Flexbox |
| CSP compliance |
No inline scripts, approved CDNs only |
No external dependencies required - widgets run in ChatGPT's sandboxed iframe.
🛑 STOP: Clarify Before Building
Never build a widget without understanding requirements. Ask these questions:
Required Clarifications
Data Shape: "What data will this widget display? Can you show the expected toolOutput structure?"
Example: { items: [{ id: 1, title: "...", status: "..." }], total: 10 }
Read vs Write: "Is this display-only or does it allow editing/submission?"
- Read only → Simple rendering, no callTool
- Write/Submit → Need form handling, callTool integration, loading states
Single vs Multi-turn: "Does the task complete in one exchange or persist across interactions?"
- Single turn → Stateless, all data in toolOutput
- Multi-turn → Need widgetState persistence, sendFollowUpMessage
Display Mode: "Which display mode fits your use case?"
inline (default) - Simple displays, ≤2 actions, in conversation flow
fullscreen - Complex workflows, editing, maps, detailed views
pip - Persistent content like video, games, live sessions
MCP Tool Name: "What tool should be called for actions?" (if interactive)
Optional Clarifications
- Design Preference: Minimal (default), branded colors, specific theme?
- Component Type: List, Map, Album, Carousel, Shop, or custom?
Before Asking
Check existing context first:
- Review conversation for prior answers about data shape or requirements
- Infer widget type from user's domain (e.g., "course app" → progress tracker)
- Check if user provided sample data or mockups
If User Skips Questions
- Required questions: Explain why needed, ask again simply
- Optional questions: Use sensible defaults (minimal design, custom type)
- Ambiguous answers: Confirm interpretation before building
Proceed only after requirements are clear.
Official Documentation
Reference these for latest patterns and complex widgets:
For complex widgets (maps, charts, 3D, video players) not covered below, fetch from these docs.
Version Note: OpenAI Apps SDK is actively evolving. When building complex widgets, fetch latest docs to verify API signatures and CSP rules haven't changed.
UX Principles Enforcement (Mandatory)
Before implementation, verify widget concept passes:
Must Follow
Must Avoid
- ❌ Long-form content in widget (let ChatGPT explain)
- ❌ Complex multi-step workflows in single widget
- ❌ Deep navigation trees
- ❌ Nested scrolling
- ❌ Standalone dashboards
If widget concept violates these, redesign before building.
See references/ux-principles.md for complete guidelines.
State Management
Reference: https://developers.openai.com/apps-sdk/build/state-management
Three State Categories
| State Type |
Owner |
Lifetime |
Example |
| Business Data |
MCP Server |
Long-lived |
Tasks, documents, user records |
| UI State |
Widget |
Message-scoped |
Selections, expanded panels, sort order |
| Cross-Session |
Backend Storage |
Persistent |
Saved filters, preferences |
Data Flow Pattern
┌─────────────────────────────────────────────────────────────┐
│ 1. User action in widget │
│ 2. Widget calls server tool (callTool) │
│ 3. Server updates authoritative data │
│ 4. Server returns new snapshot (toolOutput) │
│ 5. Widget re-renders with snapshot + local UI state │
└─────────────────────────────────────────────────────────────┘
Server is authoritative - Never let UI state diverge from server data.
Critical Constraints
- ❌ Avoid localStorage - Widget state doesn't persist there reliably
- ❌ No auto re-sync - Widget must re-apply local state on new data
- ✅ widgetState - Only persists for that message's widget instance
- ✅ <4KB tokens - Keep widgetState small (shown to model)
Quick Implementation Reference
Data Access
// Tool output (structuredContent from MCP)
const data = window.openai?.toolOutput;
// Private metadata (_meta, not sent to model)
const meta = window.openai?.toolResponseMetadata;
// Listen for updates
window.addEventListener('openai:set_globals', () => {
const newData = window.openai?.toolOutput;
});
Theme Support (Required)
const theme = window.openai?.theme ?? 'light';
// Apply system fonts: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif
Tool Invocation (When Interactive)
const result = await window.openai?.callTool('tool_name', { param: value });
const responseData = result?.structuredContent;
State Persistence (When Needed)
// Save (shown to model, <4KB)
window.openai?.setWidgetState({ selectedId: 5 });
// Read
const saved = window.openai?.widgetState;
Error Handling (Required)
// Tool call with error handling
async function safeToolCall(toolName: string, params: object) {
try {
const result = await window.openai?.callTool(toolName, params);
if (result?.isError) {
showError(result.content?.[0]?.text ?? 'Operation failed');
return null;
}
return result?.structuredContent;
} catch (err) {
showError('Connection error. Please try again.');
return null;
}
}
// Data validation
function validateToolOutput(data: unknown): data is ExpectedType {
return data != null && typeof data === 'object' && 'requiredField' in data;
}
Always handle: Missing data, malformed responses, network failures, timeouts.
See references/window-openai-api.md for complete API reference.
Component Types (Official)
Reference: https://developers.openai.com/apps-sdk/plan/components
List
- Dynamic collections with empty-state handling
- Best for: search results, task lists, notifications
- Supports filtering via conversation
Map
- Geographic data with marker clustering
- Detail panels on selection
- Best for: location-based content, store finders
- Requires fullscreen for complex interaction
Album
- Media grids with fullscreen transitions
- Best for: image galleries, portfolios
- Swipe navigation in fullscreen
Carousel
- Featured content with swipe interactions
- Best for: product highlights, recommendations
- 3-8 items for scannability
Shop
- Product browsing with checkout options
- Best for: e-commerce, booking flows
- Supports callTool for transactions
Additional Patterns
Progress Tracker
- Circular/linear progress visualization
- Module breakdown with percentages
- Expandable sections for details
Quiz/Assessment
- Question with options display
- Selection state management
- Submit via
callTool, results visualization
Form/Input
- Input validation, loading states
- Clear error display
- Submit via
callTool
For patterns not listed, fetch from official docs.
Output Checklist
Every widget must include:
Functional
Visual
UX Compliance
Reference Files
Read based on need (progressive disclosure):
| File |
When to Read |
references/window-openai-api.md |
API details, CSP configuration |
references/state-management.md |
Widget state, persistence patterns |
references/display-modes.md |
Choosing/implementing display modes |
references/ux-principles.md |
Validating design decisions |
references/react-hooks.md |
Building React widgets |
references/authentication.md |
OAuth 2.1 for user-specific data (advanced) |
Asset Templates
Copy and customize:
assets/templates/vanilla-widget/ - HTML/CSS/JS, no build step
assets/templates/react-widget/ - TypeScript with hooks
1---2name: creating-chatgpt-widgets3description: Create production-grade widgets for ChatGPT Apps using the OpenAI Apps SDK. Use when users ask to build widgets, UI components, or visual interfaces for ChatGPT applications. Supports any widget type including progress trackers, quiz interfaces, content viewers, data cards, carousels, forms, charts, dashboards, maps, video players, or custom interactive elements. IMPORTANT - Always clarify requirements before building. Creates complete implementations following official OpenAI UX/UI guidelines with window.openai integration, theme support, and accessibility.4---56# Creating ChatGPT Widgets78Create production-ready widgets for ChatGPT Apps following official OpenAI guidelines.910## What This Skill Does1112- Creates widgets for ChatGPT Apps with `window.openai` integration13- Supports all display modes (inline, fullscreen, pip)14- Implements theme support, accessibility, responsive design15- Follows official OpenAI UX/UI guidelines1617## What This Skill Does NOT Do1819- Create native mobile apps or browser extensions20- Build MCP server backend logic21- Handle user authentication (see `references/authentication.md` for patterns)22- Deploy widgets to production23- Create standalone web applications2425## Dependencies2627| Dependency | Version/Notes |28|------------|---------------|29| `window.openai` API | Required - injected by ChatGPT runtime |30| Modern browser | ES2020+, CSS Grid/Flexbox |31| CSP compliance | No inline scripts, approved CDNs only |3233**No external dependencies required** - widgets run in ChatGPT's sandboxed iframe.3435---3637## 🛑 STOP: Clarify Before Building3839**Never build a widget without understanding requirements.** Ask these questions:4041### Required Clarifications42431. **Data Shape**: "What data will this widget display? Can you show the expected `toolOutput` structure?"44 ```45 Example: { items: [{ id: 1, title: "...", status: "..." }], total: 10 }46 ```47482. **Read vs Write**: "Is this display-only or does it allow editing/submission?"49 - Read only → Simple rendering, no callTool50 - Write/Submit → Need form handling, callTool integration, loading states51523. **Single vs Multi-turn**: "Does the task complete in one exchange or persist across interactions?"53 - Single turn → Stateless, all data in toolOutput54 - Multi-turn → Need widgetState persistence, sendFollowUpMessage55564. **Display Mode**: "Which display mode fits your use case?"57 - `inline` (default) - Simple displays, ≤2 actions, in conversation flow58 - `fullscreen` - Complex workflows, editing, maps, detailed views59 - `pip` - Persistent content like video, games, live sessions60615. **MCP Tool Name**: "What tool should be called for actions?" (if interactive)6263### Optional Clarifications64656. **Design Preference**: Minimal (default), branded colors, specific theme?667. **Component Type**: List, Map, Album, Carousel, Shop, or custom?6768### Before Asking6970Check existing context first:71- Review conversation for prior answers about data shape or requirements72- Infer widget type from user's domain (e.g., "course app" → progress tracker)73- Check if user provided sample data or mockups7475### If User Skips Questions7677- **Required questions**: Explain why needed, ask again simply78- **Optional questions**: Use sensible defaults (minimal design, custom type)79- **Ambiguous answers**: Confirm interpretation before building8081**Proceed only after requirements are clear.**8283---8485## Official Documentation8687Reference these for latest patterns and complex widgets:8889| Resource | URL | Use For |90|----------|-----|---------|91| UX Principles | https://developers.openai.com/apps-sdk/concepts/ux-principles | Design decisions |92| UI Guidelines | https://developers.openai.com/apps-sdk/concepts/ui-guidelines | Visual design rules |93| **Component Types** | https://developers.openai.com/apps-sdk/plan/components | List, Map, Album, Carousel, Shop patterns |94| Build ChatGPT UI | https://developers.openai.com/apps-sdk/build/chatgpt-ui | Implementation, CSP, bundling |95| **State Management** | https://developers.openai.com/apps-sdk/build/state-management | Widget state, persistence patterns |96| Authentication | https://developers.openai.com/apps-sdk/build/auth | OAuth 2.1 for user-specific data *(advanced)* |97| UI Components | https://openai.github.io/apps-sdk-ui/ | Pre-built UI library |98| Example Apps | https://github.com/openai/openai-apps-sdk-examples | Reference implementations |99100**For complex widgets** (maps, charts, 3D, video players) not covered below, fetch from these docs.101102> **Version Note**: OpenAI Apps SDK is actively evolving. When building complex widgets, fetch latest docs to verify API signatures and CSP rules haven't changed.103104---105106## UX Principles Enforcement (Mandatory)107108Before implementation, verify widget concept passes:109110### Must Follow111- [ ] **Extract, Don't Port** - Atomic actions only, not full app ports112- [ ] **Inline by Default** - Use fullscreen only when justified113- [ ] **Max 2 Actions** - Limit buttons per inline card114- [ ] **No Widget Navigation** - Conversation handles routing115- [ ] **No Duplicated Functions** - Don't recreate chat input, settings116117### Must Avoid118- ❌ Long-form content in widget (let ChatGPT explain)119- ❌ Complex multi-step workflows in single widget120- ❌ Deep navigation trees121- ❌ Nested scrolling122- ❌ Standalone dashboards123124**If widget concept violates these, redesign before building.**125126See `references/ux-principles.md` for complete guidelines.127128---129130## State Management131132> Reference: https://developers.openai.com/apps-sdk/build/state-management133134### Three State Categories135136| State Type | Owner | Lifetime | Example |137|------------|-------|----------|---------|138| **Business Data** | MCP Server | Long-lived | Tasks, documents, user records |139| **UI State** | Widget | Message-scoped | Selections, expanded panels, sort order |140| **Cross-Session** | Backend Storage | Persistent | Saved filters, preferences |141142### Data Flow Pattern143144```145┌─────────────────────────────────────────────────────────────┐146│ 1. User action in widget │147│ 2. Widget calls server tool (callTool) │148│ 3. Server updates authoritative data │149│ 4. Server returns new snapshot (toolOutput) │150│ 5. Widget re-renders with snapshot + local UI state │151└─────────────────────────────────────────────────────────────┘152```153154**Server is authoritative** - Never let UI state diverge from server data.155156### Critical Constraints157158- ❌ **Avoid localStorage** - Widget state doesn't persist there reliably159- ❌ **No auto re-sync** - Widget must re-apply local state on new data160- ✅ **widgetState** - Only persists for that message's widget instance161- ✅ **<4KB tokens** - Keep widgetState small (shown to model)162163---164165## Quick Implementation Reference166167### Data Access168```typescript169// Tool output (structuredContent from MCP)170const data = window.openai?.toolOutput;171172// Private metadata (_meta, not sent to model)173const meta = window.openai?.toolResponseMetadata;174175// Listen for updates176window.addEventListener('openai:set_globals', () => {177 const newData = window.openai?.toolOutput;178});179```180181### Theme Support (Required)182```typescript183const theme = window.openai?.theme ?? 'light';184// Apply system fonts: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif185```186187### Tool Invocation (When Interactive)188```typescript189const result = await window.openai?.callTool('tool_name', { param: value });190const responseData = result?.structuredContent;191```192193### State Persistence (When Needed)194```typescript195// Save (shown to model, <4KB)196window.openai?.setWidgetState({ selectedId: 5 });197198// Read199const saved = window.openai?.widgetState;200```201202### Error Handling (Required)203```typescript204// Tool call with error handling205async function safeToolCall(toolName: string, params: object) {206 try {207 const result = await window.openai?.callTool(toolName, params);208 if (result?.isError) {209 showError(result.content?.[0]?.text ?? 'Operation failed');210 return null;211 }212 return result?.structuredContent;213 } catch (err) {214 showError('Connection error. Please try again.');215 return null;216 }217}218219// Data validation220function validateToolOutput(data: unknown): data is ExpectedType {221 return data != null && typeof data === 'object' && 'requiredField' in data;222}223```224225**Always handle**: Missing data, malformed responses, network failures, timeouts.226227See `references/window-openai-api.md` for complete API reference.228229---230231## Component Types (Official)232233> Reference: https://developers.openai.com/apps-sdk/plan/components234235### List236- Dynamic collections with empty-state handling237- Best for: search results, task lists, notifications238- Supports filtering via conversation239240### Map241- Geographic data with marker clustering242- Detail panels on selection243- Best for: location-based content, store finders244- Requires fullscreen for complex interaction245246### Album247- Media grids with fullscreen transitions248- Best for: image galleries, portfolios249- Swipe navigation in fullscreen250251### Carousel252- Featured content with swipe interactions253- Best for: product highlights, recommendations254- 3-8 items for scannability255256### Shop257- Product browsing with checkout options258- Best for: e-commerce, booking flows259- Supports callTool for transactions260261---262263## Additional Patterns264265### Progress Tracker266- Circular/linear progress visualization267- Module breakdown with percentages268- Expandable sections for details269270### Quiz/Assessment271- Question with options display272- Selection state management273- Submit via `callTool`, results visualization274275### Form/Input276- Input validation, loading states277- Clear error display278- Submit via `callTool`279280For patterns not listed, fetch from official docs.281282---283284## Output Checklist285286Every widget must include:287288### Functional289- [ ] `window.openai` data access with null checks290- [ ] Event listener for `openai:set_globals`291- [ ] Loading state (before data arrives)292- [ ] Error state (when data.isError)293- [ ] Empty state (when no data)294295### Visual296- [ ] Theme support (light/dark via `window.openai.theme`)297- [ ] System fonts (no custom fonts unless content area)298- [ ] WCAG AA contrast (4.5:1 text, 3:1 UI)299- [ ] Responsive layout (desktop + mobile breakpoints)300- [ ] Focus indicators for interactive elements301- [ ] Keyboard navigation support302303### UX Compliance304- [ ] Follows "Extract, Don't Port"305- [ ] Inline mode unless justified306- [ ] ≤2 actions per card307- [ ] No hardcoded data308- [ ] Clear JSON payload structure309310---311312## Reference Files313314Read based on need (progressive disclosure):315316| File | When to Read |317|------|--------------|318| `references/window-openai-api.md` | API details, CSP configuration |319| `references/state-management.md` | Widget state, persistence patterns |320| `references/display-modes.md` | Choosing/implementing display modes |321| `references/ux-principles.md` | Validating design decisions |322| `references/react-hooks.md` | Building React widgets |323| `references/authentication.md` | OAuth 2.1 for user-specific data *(advanced)* |324325## Asset Templates326327Copy and customize:328- `assets/templates/vanilla-widget/` - HTML/CSS/JS, no build step329- `assets/templates/react-widget/` - TypeScript with hooks