xmcp-dev/skills

widget-design

Best practices for designing UI widgets in xmcp. Use when creating interactive widgets for GPT Apps or MCP Apps, choosing between React components and template literals, designing widget layouts, handling state and data fetching, or troubleshooting widget rendering issues.

First seen Jan 25, 2026

Installation

$ npx skills add xmcp-dev/skills --skill widget-design

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from xmcp-dev/skills.

npx skills add xmcp-dev/skills

Browse all from xmcp-dev/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 5,769 B
  • docs SUMMARY.md 294 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 42 installs

SKILL.md

Widget Design Best Practices

Decision Framework

Platform Selection

Choose based on target client:

  • GPT Apps: Target is ChatGPT. Requires _meta.openai with widgetAccessible: true.
  • MCP Apps: Target is any ext-apps client. Minimal config, works automatically.

Handler Type Selection

Scenario Handler Reason
User interaction needed (buttons, inputs) React (.tsx) State management with hooks
Display external widget library Template literal Just load scripts/styles
Dynamic content from tool params React Props flow naturally
Static HTML with no state Template literal Simpler, less overhead

Rule of thumb: If unsure, start with React. Converting later is harder than starting simple.

Widget Design Principles

1. Widgets Are Not Web Apps

Widgets render inline in conversations. Design constraints:

  • No navigation: Single-screen experience only
  • Limited width: ~600-700px max in most clients
  • Sandboxed: External resources need CSP declarations
  • Ephemeral: May be re-rendered, don't rely on persistence

2. Immediate Value

Show useful content without requiring user action:

// Bad: Requires click to see anything
export default function Widget() {
  const [data, setData] = useState(null);
  return <button onClick={fetchData}>Load Data</button>;
}

// Good: Shows data immediately
export default function Widget({ query }) {
  const [data, setData] = useState(null);
  useEffect(() => { fetchData(query).then(setData); }, [query]);
  return data ? <Results data={data} /> : <Loading />;
}

3. Visual Hierarchy in Limited Space

With limited width, hierarchy matters more:

<div className="space-y-4">
  {/* Label: small, muted, uppercase */}
  <div className="text-sm text-zinc-500 uppercase tracking-wider">Temperature</div>
  
  {/* Value: large, prominent */}
  <div className="text-5xl font-light">72°F</div>
  
  {/* Supporting: medium, secondary */}
  <div className="text-zinc-400">Feels like 68°F</div>
</div>

4. Every Interactive Element Needs Feedback

Users need visual confirmation that elements are interactive:

// Bad: No visual feedback
<button className="px-4 py-2 bg-white/10">Click</button>

// Good: Hover + transition
<button className="px-4 py-2 bg-white/10 hover:bg-white/20 border border-white/10 hover:border-white/20 transition-all duration-200">
  Click
</button>

State Management Guidelines

Keep State Local and Simple

Widgets are isolated. No Redux, Zustand, or external state.

// Good: Local state with hooks
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

Always Handle Three States

Every async operation has three states. Handle all of them:

export default function Widget() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchData()
      .then(setData)
      .catch(e => setError(e.message))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <Loading />;
  if (error) return <Error message={error} />;
  return <Display data={data} />;
}

Fetch on Mount, Not on Click

Widgets should show value immediately:

// Bad: User must click to see data
<button onClick={() => fetchData()}>Load</button>

// Good: Fetch automatically
useEffect(() => { fetchData(); }, []);

Common Mistakes

1. Missing widgetAccessible (GPT Apps)

Widget won't render without this:

// Broken
_meta: { openai: { toolInvocation: { ... } } }

// Fixed
_meta: { openai: { widgetAccessible: true, toolInvocation: { ... } } }

2. External Fetch Without CSP

Requests blocked silently:

// Broken: fetch fails silently
fetch('{{WEATHER_API_URL}}');

// Fixed: declare in metadata
_meta: {
  openai: {
    widgetCSP: { connect_domains: ["{{WEATHER_API_BASE_URL}}"] }
  }
}

3. Hardcoded Dimensions

Widgets break on different screen sizes:

// Bad
<div style={{ width: '800px' }}>...</div>

// Good
<div className="w-full max-w-2xl mx-auto">...</div>

4. No Error Boundaries

Crashes show blank widget:

// Always wrap risky operations
try {
  const result = JSON.parse(data);
} catch {
  return <Error message="Invalid data format" />;
}

Platform-Specific Notes

GPT Apps: Structured Content for Widget Communication

Pass data from tool to widget:

return {
  structuredContent: { game: "doom", url: "..." },
  content: [{ type: "text", text: "Launching DOOM..." }],
};

Widget reads via useToolOutput() hook.

MCP Apps: Minimal Config

MCP Apps work with just a React component:

// This is enough for MCP Apps
export const metadata = { name: "widget", description: "..." };
export default function Widget() { return <div>Hello</div>; }

Quick Reference

GPT Apps Checklist

  • widgetAccessible: true in metadata
  • toolInvocation messages under 64 chars
  • CSP for external domains

References

See [references/design-principles.md](references/design-principles.md) for:

  • Complete widget examples (counter, weather, arcade)
  • Component patterns (cards, buttons, tabs)
  • CSS Modules examples
  • GPT App submission requirements
  • Project structure templates