Skip to content
Frontend
Skill

/creating-styled-wrappers

Creates styled wrapper components that compose headless/base compound components. Use when refactoring styled components to use base primitives, implementing opinionated design systems on top of headless components, or when the user mentions "use base components", "compose

From plugin
tambo
11k8 skills2 agents4 commands

Claude Code

$ npx -y skills add tambo-ai/tambo --skill creating-styled-wrappers --agent claude-code

SKILL.md

creating-styled-wrappers.SKILL.md
name: creating-styled-wrappers
description: Creates styled wrapper components that compose headless/base compound components. Use when refactoring styled components to use base primitives, implementing opinionated design systems on top of headless components, or when the user mentions "use base components", "compose primitives", "styled wrapper", or "refactor to use base".
metadata:
  internal: true

Styling Compound Wrappers

Create styled wrapper components that compose headless base compound components. This skill complements `building-compound-components` (which builds the base primitives) by focusing on **how to properly consume and wrap them** with styling and additional behavior.

**Real-world example**: See [references/real-world-example.md](references/real-world-example.md) for a complete before/after MessageInput refactoring.

Core Principle: Compose, Don't Duplicate

Styled wrappers should **compose** base components, not **re-implement** their logic.

// WRONG - re-implementing what base already does
const StyledInput = ({ children, className }) => {
  const { value, setValue, submit } = useTamboThreadInput(); // Duplicated!
  const [isDragging, setIsDragging] = useState(false); // Duplicated!
  const handleDrop = useCallback(/* ... */); // Duplicated!

  return (
    <form onDrop={handleDrop} className={className}>
      {children}
    </form>
  );
};

// CORRECT - compose the base component
const StyledInput = ({ children, className, variant }) => {
  return (
    <BaseInput.Root className={cn(inputVariants({ variant }), className)}>
      <BaseInput.Content className="rounded-xl data-[dragging]:border-dashed">
        {children}
      </BaseInput.Content>
    </BaseInput.Root>
  );
};

Refactoring Workflow

Copy this checklist and track progress:

Styled Wrapper Refactoring:
- [ ] Step 1: Identify duplicated logic
- [ ] Step 2: Import base components
- [ ] Step 3: Wrap with Base Root
- [ ] Step 4: Apply state-based styling and behavior
- [ ] Step 5: Wrap sub-components with styling
- [ ] Step 6: Final verification

Step 1: Identify Duplicated Logic

Look for patterns that indicate logic should come from base:

  • SDK hooks (`useTamboThread`, `useTamboThreadInput`, etc.)
  • Context creation (`React.createContext`)
  • State management that mirrors base component state
  • Event handlers (drag, submit, etc.) that base components handle

Step 2: Import Base Components

import { MessageInput as MessageInputBase } from "@tambo-ai/react-ui-base/message-input";

Step 3: Wrap with Base Root

Replace custom context/state management with the base Root:

// Before
const MessageInput = ({ children, variant }) => {
  return (
    <MessageInputInternal variant={variant}>{children}</MessageInputInternal>
  );
};

// After
const MessageInput = ({ children, variant, className }) => {
  return (
    <MessageInputBase.Root className={cn(variants({ variant }), className)}>
      {children}
    </MessageInputBase.Root>
  );
};

Step 4: Apply State-Based Styling and Behavior

State access follows a hierarchy — use the simplest option that works:

1. **Data attributes** (preferred for styling) — base components expose `data-*` attributes 2. **Render props** (for behavior changes) — use when rendering different components 3. **Context hooks** (for sub-components) — OK for styled sub-components needing deep context access

// BEST - data-* classes for styling, render props only for behavior
// Note: use `data-[dragging]:*` syntax (v3-compatible), not `data-dragging:*` (v4 only)
const StyledContent = ({ children }) => (
  <BaseComponent.Content
    className={cn(
      "group rounded-xl border",
      "data-[dragging]:border-dashed data-[dragging]:border-emerald-400",
    )}
  >
    {({ elicitation, resolveElicitation }) => (
      <>
        {/* Drop overlay uses group-data-* for styling */}
        <div className="hidden group-data-[dragging]:flex absolute inset-0 bg-emerald-50/90">
          <p>Drop files here</p>
        </div>

        {elicitation ? (
          <ElicitationUI
            request={elicitation}
            onResponse={resolveElicitation}
          />
        ) : (
          children
        )}
      </>
    )}
  </BaseComponent.Content>
);

// OK - styled sub-components can use context hook for deep access
const StyledTextarea = ({ placeholder }) => {
  const { value, setValue, handleSubmit, editorRef } = useMessageInputContext();
  return (
    <CustomEditor
      ref={editorRef}
      value={value}
      onChange={setValue}
      onSubmit={handleSubmit}
      placeholder={placeholder}
    />
  );
};

**When to use context hooks vs render props:**

  • Render props: when the parent wrapper needs state for behavior changes
  • Context hooks: when a styled sub-component needs values not exposed via render props

Step 5: Wrap Sub-Components

// Submit button
const SubmitButton = ({ className, children }) => (
  <BaseComponent.SubmitButton className={cn("w-10 h-10 rounded-lg", className)}>
    {({ showCancelButton }) =>
      children ?? (showCancelButton ? <Square /> : <ArrowUp />)
    }
  </BaseComponent.SubmitButton>
);

// Error
const Error = ({ className }) => (
  <BaseComponent.Error className={cn("text-sm text-destructive", className)} />
);

// Staged images - base pre-computes props array, just iterate
const StagedImages = ({ className }) => (
  <BaseComponent.StagedImages className={cn("flex gap-2", className)}>
    {({ images }) =>
      images.map((imageProps) => (
        <ImageBadge key={imageProps.image.id} {...imageProps} />
      ))
    }
  </BaseComponent.StagedImages>
);

Step 6: Final Verification

Final Checks:
- [ ] No duplicate context creation
- [ ] No duplicate SDK hooks in root wrappers
- [ ] No duplicate state management or event handlers
- [ ] Base namespace imported and `Base.Root` used as wrapper
- [ ] `data-*` classes used for styling (with `group-data-*`
Read more
@tambo-ai
Ships withtambo

Generative UI SDK for React

11k8 skills2 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
11.2k
Star history of tambo-ai/tambo on GitHub, a chart by star-history.com

Other skills on tambo.

ai-sdk-model-manager

ai-sdk-model-manager

Manages AI SDK model configurations - updates packages, identifies missing models, adds new models with research, and updates…

@tambo-ai@tambo-aiInstall Skill
api-resource-lifecycle

api-resource-lifecycle

Guides CRUD operations for API resources with cascading dependencies, descriptive validation, and orphan prevention. Use when…

@tambo-ai@tambo-aiInstall Skill
building-settings-ui

building-settings-ui

Use this skill when adding or modifying settings UI in Tambo Cloud. Covers where a new settings section belongs (Agent tab vs…

@tambo-ai@tambo-aiInstall Skill
compound-components

compound-components

Creates unstyled compound components that separate business logic from styles. Use when building headless UI primitives, creating…

@tambo-ai@tambo-aiInstall Skill
validating-accessibility

validating-accessibili…

Use this skill when creating, modifying, or reviewing any .tsx component in apps/web, even if the user doesn't mention…

@tambo-ai@tambo-aiInstall Skill
building-with-tambo

building-with-tambo

Integrates Tambo into existing React apps — detects tech stack, installs @tambo-ai/react, wires TamboProvider, registers…

@tambo-ai@tambo-aiInstall Skill

More skills from other plugins.

next-bundle-optimizer
Auto-invoked

next-bundle-optimizer

Audit and reduce Next.js browser initial-load work. Use for slow route startup, oversized client bundles, duplicate browser…

nextjs1d143k
@vercel@vercelInstall Skill
migrate-radix-to-base

migrate-radix-to-base

Migrates React projects and components from Radix UI to Base UI. Use when asked to migrate from radix, move to base-ui, convert…

ui10d127k
@shadcn-ui@shadcn-uiInstall Skill
chrome-release-cls

chrome-release-cls

Given a Chrome Releases blog post URL (chromereleases.googleblog.com), extract every CVE/bug and find the underlying Gerrit CL…

electron1d124k
@electron@electronInstall Skill
taste-skill
Auto-invoked

taste-skill

tasteskill v2, the anti-slop frontend skill. Art direction for websites, distilled from a study of 247 curated gallery sites (One…

@leonxlnx@leonxlnxInstall Skill

Questions about creating-styled-wrappers.

Which coding agents can use creating-styled-wrappers?
creating-styled-wrappers 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.