CR

creating-styled-wrappers

Creates styled wrapper components that apply external styles to base primitives while keeping logic contained.

Install

mkdir -p .claude/skills/creating-styled-wrappers && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4532" && unzip -o skill.zip -d .claude/skills/creating-styled-wrappers && rm skill.zip

Installs to .claude/skills/creating-styled-wrappers

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

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".
331 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Composes base headless components
  • Applies external styling to wrapper components
  • Extracts logic into base primitives
  • Validates state-based style application
  • Refactors legacy components to primitives

How it works

Identifies duplicated logic between styled components and base primitives, then mandates moving business logic into the base layer while delegating styling to the wrapper.

Inputs & outputs

You give it
Styled component code and base primitive
You get back
Refactored composition-based wrapper component

When to use creating-styled-wrappers

  • Refactoring legacy styled components
  • Implementing design system wrappers
  • Building opinionated UI on headless primitives
  • Optimizing component composition

About this skill

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 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-*` for children)
- [ ] Render props used only for rendering behavior changes
- [ ] Base sub-components wrapped with styling
- [ ] Icon factories passed from styled layer to base hooks
- [ ] Visual sub-components and CSS variants stay in styled layer

What Belongs in Styled Layer

Icon Factories

When base hooks need icons, pass a factory function:

// Base hook accepts optional icon factory
export function useCombinedResourceList(
  providers: ResourceProvider[] | undefined,
  search: string,
  createMcpIcon?: (serverName: string) => React.ReactNode,
) {
  /* ... */
}

// Styled layer provides the factory
const resources = useCombinedResourceList(providers, search, (serverName) => (
  <McpServerIcon name={serverName} className="w-4 h-4" />
));

CSS Variants

const inputVariants = cva("w-full", {
  variants: {
    variant: {
      default: "",
      solid: "[&>div]:shadow-xl [&>div]:ring-1",
      bordered: "[&>div]:border-2",
    },
  },
});

Layout Logic, Visual Sub-Components, Custom Data Fetching

These all stay in the styled layer. Base handles behavior; styled handles presentation.

Type Handling

Handle ref type differences between base and styled components:

// Base context may have RefObject<T | null>
// Styled component may need RefObject<T>
<TextEditor ref={editorRef as React.RefObject<TamboEditor>} />

Anti-Patterns

  • Re-implementing base logic - if base handles it, compose it
  • Using render props for styling - prefer data-* classes; render props are for behavior changes
  • Duplicating context in wrapper - use base Root which provides context
  • Hardcoding icons in base hooks - use factory functions to keep styling in styled layer

When not to use it

  • When building base components from scratch
  • When performance requirements dictate raw CSS without composition

Limitations

  • Requires a defined headless component library
  • Requires manual cleanup of redundant internal state

How it compares

It focuses on structural composition rather than overriding styles through cascading CSS overrides.

Compared to similar skills

creating-styled-wrappers side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
creating-styled-wrappers (this skill)14moNo flagsIntermediate
nextjs-developer3282moNo flagsAdvanced
landing-page-guide-v2488moReviewIntermediate
frontend-developer274moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

nextjs-developer

zenobi-us

Expert Next.js developer mastering Next.js 14+ with App Router and full-stack features. Specializes in server components, server actions, performance optimization, and production deployment with focus on building fast, SEO-friendly applications.

328531

landing-page-guide-v2

bear2u

Create distinctive, high-converting landing pages that combine proven conversion elements with exceptional design quality. Build beautiful, memorable landing pages using Next.js 14+ and ShadCN UI that avoid generic AI aesthetics while following the 11 essential elements framework.

48105

frontend-developer

sickn33

Build React components, implement responsive layouts, and handle client-side state management. Masters React 19, Next.js 15, and modern frontend architecture. Optimizes performance and ensures accessibility. Use PROACTIVELY when creating UI components or fixing frontend issues.

2782

nextjs-best-practices

davila7

Next.js App Router principles. Server Components, data fetching, routing patterns.

3164

frontend-code-review

langgenius

Trigger when the user requests a review of frontend files (e.g., `.tsx`, `.ts`, `.js`). Support both pending-change reviews and focused file reviews while applying the checklist rules.

1174

frontend-prompt-generator

gharam1234

Generate structured prompts for frontend development tasks following established patterns. Use when the user requests prompts for wireframes, UI implementation, data binding, or routing functionality in React/Next.js projects with specific formatting requirements (Cursor rules, file paths, test-driven development).

679

Search skills

Search the agent skills registry