Skip to content
Backend
Skill

/ui-widgets

Best practices for linking tools to interactive frontend widgets using @Widget and @nitrostack/widgets SDK (including state sync, tool calling, display modes, media queries, and chat actions).

From plugin
nitrostack
2.6k14 skills4 agents3 MCP

Claude Code

$ npx -y skills add nitrocloudofficial/nitrostack --skill ui-widgets --agent claude-code

SKILL.md

ui-widgets.SKILL.md
name: nitrostack-ui-widgets
description: Best practices for linking tools to interactive frontend widgets using @Widget and @nitrostack/widgets SDK (including state sync, tool calling, display modes, media queries, and chat actions).

When to Use

Use this skill when designing, building, or modifying interactive user interface widgets that display custom React content inside AI clients or NitroStudio.

---

1. Backend Definition (`@Widget`)

To display a React-based widget for a tool's output, decorate the tool method with `@Widget`.

Options:

  • **String Route**: A simple string representing the route identifier in the frontend React app (e.g. `'product-card'`).
  • **Object Route**: Object including:
  • `route` (required): The route path.
  • `domain` (optional): Allowed sandbox domain.
  • `csp` (optional): Content Security Policy guidelines.

Example:

import { Tool, Widget, z } from '@nitrostack/core';

export class CatalogTools {
  @Tool({
    name: 'fetch_product',
    description: 'Get product information by barcode.',
    inputSchema: z.object({ barcode: z.string() }),
  })
  @Widget('product-details') // Maps to the "product-details" frontend component
  async fetchProduct(input: { barcode: string }) {
    return {
      name: 'Super Nitro Energy Drink',
      price: 2.99,
      sku: input.barcode,
    };
  }
}

---

2. Frontend React Widget (`@nitrostack/widgets`)

In your React widget frontend application (typically a Next.js client component), use the `useWidgetSDK` hook to receive input data from the client host.

React Component Example:

'use client';

import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

interface ProductData {
  name: string;
  price: number;
  sku: string;
}

export default function ProductDetailsWidget() {
  const { isReady, getToolOutput, theme } = useWidgetSDK();
  const data = getToolOutput<ProductData>();

  if (!isReady) {
    return <div className="loading">Connecting to host...</div>;
  }

  if (!data) {
    return <div className="error">No product data received.</div>;
  }

  return (
    <div className={`product-card ${theme === 'dark' ? 'dark' : 'light'}`}>
      <h3>{data.name}</h3>
      <p className="price">${data.price.toFixed(2)}</p>
      <span className="sku">SKU: {data.sku}</span>
    </div>
  );
}

---

3. State Management & Synchronization (`useWidgetState`)

Use `useWidgetState` to manage and persist client-side widget state (e.g. selected tabs, filter values, input states). This state automatically synchronizes with the host application context, persisting it across page re-renders.

Example:

import React from 'react';
import { useWidgetState } from '@nitrostack/widgets';

export default function StationPanelWidget() {
  const [state, setState] = useWidgetState(() => ({
    selectedTab: 'overview',
    showExtendedInfo: false,
  }));

  return (
    <div>
      <button onClick={() => setState({ ...state, selectedTab: 'alerts' })}>
        View Alerts
      </button>
      <p>Current Tab: {state?.selectedTab}</p>
    </div>
  );
}

---

4. Calling Core Tools from Widgets (`callTool`)

You can invoke other backend MCP tools directly from the frontend widget using `callTool`. This is useful for tool chaining or triggering detailed audits.

Example:

import React, { useState } from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

export default function SystemDiagnostics() {
  const { callTool, isReady } = useWidgetSDK();
  const [isRunning, setIsRunning] = useState(false);
  const [result, setResult] = useState<string | null>(null);

  const runDiagnostic = async () => {
    if (!isReady) return;
    setIsRunning(true);
    try {
      const response = await callTool('run_diagnostic', { system: 'oxygen_scrubber' });
      setResult(response.result as string);
    } catch (err) {
      setResult('Diagnostic execution failed.');
    } finally {
      setIsRunning(false);
    }
  };

  return (
    <button onClick={runDiagnostic} disabled={isRunning}>
      {isRunning ? 'Running...' : 'Run Diagnostics'}
    </button>
  );
}

---

5. Layout & Display Controls

Widgets can dynamically request size mode changes (fullscreen, inline, picture-in-picture) and adapt layouts to safe areas (dynamic islands/notches) or maximum height constraints.

Key Methods:

  • `requestFullscreen()`: Switch host widget display to fullscreen.
  • `requestInline()`: Switch host widget display back to inline.
  • `requestPip()`: Float widget in Picture-in-Picture.
  • `requestClose()`: Dismiss the widget completely.

Example:

import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

export default function StatusBoard() {
  const { 
    requestFullscreen, 
    requestInline, 
    requestClose,
    displayMode,   // Reactive property ('fullscreen' | 'inline' | 'pip')
    maxHeight,     // Reactive maxHeight constraint (in pixels)
    getSafeArea    // Insets data: { top, right, bottom, left }
  } = useWidgetSDK();

  const safeArea = getSafeArea() || { top: 0, bottom: 0 };

  return (
    <div style={{ maxHeight: maxHeight || 400, paddingTop: safeArea.top }}>
      <h3>Mode: {displayMode}</h3>
      <button onClick={requestFullscreen}>Fullscreen</button>
      <button onClick={requestInline}>Collapse</button>
      <button onClick={requestClose}>Dismiss Widget</button>
    </div>
  );
}

---

6. Chat Navigation & Actions

Widgets can interact with the host chat pane using external browser links and follow-up prompts.

Key Methods:

  • `openExternal(url)`: Open the target URL safely in the user's primary external browser.
  • `sendFollowUpMessage(prompt)`: Insert a message into the chat flow, automatically submitting it to the LLM agent.

Example:

import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

export default function MissionControl() {
  const { openExternal, sendF
Read more
@nitrocloudofficial
Ships withnitrostack

The full-stack TypeScript framework to build, test, and deploy production-ready MCP servers and AI-native apps.

2.6k14 skills4 agents3 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
2.6k
Star history of nitrocloudofficial/nitrostack on GitHub, a chart by star-history.com

Other skills on nitrostack.

More skills from other plugins.

cli

cli

SpacetimeDB CLI reference for initializing projects, building modules, publishing databases, querying data, and managing servers

@clockworklabs@clockworklabsInstall Skill
backend

backend

Use this skill when contributing to InsForge's backend package. This is for maintainers editing backend routes, services,…

@insforge@insforgeInstall Skill
exa-agent

exa-agent

Use Exa Agent for multi-step web research, list-building, enrichment, structured output, run continuation, and coverage…

exa9d5.2k
@exa-labs@exa-labsInstall Skill
fde-ontology

fde-ontology

多源输入→本体检出→AI审核→场景升级→代码生成 自动化管线

dgiot4w4.9k
@dgiot@dgiotInstall Skill

Questions about ui-widgets.

Which coding agents can use ui-widgets?
ui-widgets installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.