ai-sdk-model-manager
Manages AI SDK model configurations - updates packages, identifies missing models, adds new models with research, and updates documentation
Creates unstyled compound components that separate business logic from styles. Use when building headless UI primitives, creating component libraries, implementing Radix-style namespaced components, or when the user mentions "compound components", "headless", "unstyled",
$ npx -y skills add tambo-ai/tambo --skill compound-components --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/compound-componentsContext preview
The summary Claude sees to decide when to auto-load this skill.
Creates unstyled compound components that separate business logic from styles. Use when building headless UI primitives, creating component libraries, implementing Radix-style namespaced components, or when the user mentions "compound components", "headless", "unstyled",
name: building-compound-components description: Creates unstyled compound components that separate business logic from styles. Use when building headless UI primitives, creating component libraries, implementing Radix-style namespaced components, or when the user mentions "compound components", "headless", "unstyled", "primitives", or "render props". metadata: internal: true
Create unstyled, composable React components following the Radix UI / Base UI pattern. Components expose behavior via context while consumers control rendering.
These rules are specific to this codebase and override general patterns.
Hooks are implementation details, not public API. **Never export hooks from the index.**
// index.tsx - CORRECT
export const Component = {
Root: ComponentRoot,
Content: ComponentContent,
};
export type { ComponentRootProps, ComponentContentRenderProps };
// index.tsx - WRONG
export { useComponentContext }; // Don't export hooksConsumers access state via **render props**, not hooks. When styled wrappers in the **same package** need hook access, import directly from the source file:
import { useComponentContext } from "../base/component/component-context";Base components can use `@tambo-ai/react` SDK hooks (components require Tambo provider anyway). **Custom data fetching logic** (combining sources, external providers) belongs in the styled layer.
// OK - SDK hooks in primitive
const Root = ({ children }) => {
const { value, setValue, submit } = useTamboThreadInput();
const { isIdle, cancel } = useTamboThread();
return <Context.Provider value={{ value, setValue, isIdle }}>{children}</Context.Provider>;
};
// WRONG - custom data fetching in primitive
const Textarea = ({ resourceProvider }) => {
const { data: mcpResources } = useTamboMcpResourceList(search);
const externalResources = useFetchExternal(resourceProvider);
const combined = [...mcpResources, ...externalResources];
return <div>{combined.map(...)}</div>;
};When exposing collections via render props, **pre-compute all props in a memoized array** rather than providing a getter function.
// AVOID - getter function pattern
const Items = ({ children }) => {
const { rawItems, selectedId, removeItem } = useContext();
const getItemProps = (index: number) => ({
/* new object every call */
});
return children({ items: rawItems, getItemProps });
};
// PREFERRED - pre-computed array
const Items = ({ children }) => {
const { rawItems, selectedId, removeItem } = useContext();
const items = React.useMemo<ItemRenderProps[]>(
() =>
rawItems.map((item, index) => ({
item,
index,
isSelected: selectedId === item.id,
onSelect: () => setSelectedId(item.id),
onRemove: () => removeItem(item.id),
})),
[rawItems, selectedId, removeItem],
);
return children({ items });
};Copy this checklist and track progress:
Compound Component Progress: - [ ] Step 1: Create context file - [ ] Step 2: Create Root component - [ ] Step 3: Create consumer components - [ ] Step 4: Create namespace export (index.tsx) - [ ] Step 5: Verify all guidelines met
my-component/ ├── index.tsx ├── component-context.tsx ├── component-root.tsx ├── component-item.tsx └── component-content.tsx
Create a context with a null default and a hook that throws on missing provider:
// component-context.tsx
const ComponentContext = React.createContext<ComponentContextValue | null>(
null,
);
export function useComponentContext() {
const context = React.useContext(ComponentContext);
if (!context) {
throw new Error("Component parts must be used within Component.Root");
}
return context;
}
export { ComponentContext };Root manages state and provides context. Use `forwardRef`, support `asChild` via Radix `Slot`, and expose state via data attributes:
// component-root.tsx
export const ComponentRoot = React.forwardRef<
HTMLDivElement,
ComponentRootProps
>(({ asChild, defaultOpen = false, children, ...props }, ref) => {
const [isOpen, setIsOpen] = React.useState(defaultOpen);
const Comp = asChild ? Slot : "div";
return (
<ComponentContext.Provider
value={{ isOpen, toggle: () => setIsOpen(!isOpen) }}
>
<Comp ref={ref} data-state={isOpen ? "open" : "closed"} {...props}>
{children}
</Comp>
</ComponentContext.Provider>
);
});
ComponentRoot.displayName = "Component.Root";Choose the composition pattern based on need:
**Direct children** (simplest, for static content):
const Content = ({ children, className, ...props }) => {
const { data } = useComponentContext();
return (
<div className={className} {...props}>
{children}
</div>
);
};**Render prop** (when consumer needs internal state):
const Content = ({ children, ...props }) => {
const { data, isLoading } = useComponentContext();
const content =
typeof children === "function" ? children({ data, isLoading }) : children;
return <div {...props}>{content}</div>;
};**Sub-context** (for lists where each item needs own context):
const Steps = ({ children }) => {
const { reasoning } = useMessageContext();
return (
<StepsContext.Provider value={{ steps: reasoning }}>
{children}
</StepsContext.Provider>
);
};
const Step = ({ children, index }) => {
const { steps } = useStepsContext();
return (
<StepContext.Provider value={{ step: steps[index], index }}>
{children}
</StepContext.Provider>
);
};// index.tsx
export const Component = {
Root: ComponentRoot,
TriggRepo: tambo-ai/tambo
Manages AI SDK model configurations - updates packages, identifies missing models, adds new models with research, and updates documentation
Guides CRUD operations for API resources with cascading dependencies, descriptive validation, and orphan prevention. Use when adding delete/remove operations,…
Use this skill when adding or modifying settings UI in Tambo Cloud. Covers where a new settings section belongs (Agent tab vs Settings tab), and the component…
Creates styled wrapper components that compose headless/base compound components. Use when refactoring styled components to use base primitives, implementing…
Use this skill when creating, modifying, or reviewing any .tsx component in apps/web, even if the user doesn't mention "accessibility." Covers semantic HTML,…
Integrates Tambo into existing React apps — detects tech stack, installs @tambo-ai/react, wires TamboProvider, registers components with Zod schemas, and sets…