# Component Patterns

> React component patterns with Ark UI, forwardRef, and TypeScript for the design system Use when this capability is needed.

- Skill: `tomevault-io/component-patterns-4` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/component-patterns-4`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/component-patterns-4/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/component-patterns-4

---


# Component Patterns

## When to Use This Skill

- Creating a new component
- Wrapping Ark UI primitives
- Setting up TypeScript types for variants
- Understanding the component → recipe relationship

## Standard Component Structure

```
src/components/Button/
├── Button.tsx           # Component implementation
├── Button.stories.tsx   # Storybook stories
└── index.ts             # Barrel export
```

## Component Template

```tsx
// src/components/Button/Button.tsx
import { forwardRef } from 'react';
import { Button as ArkButton } from '@ark-ui/react';
import { button, type ButtonVariantProps } from 'styled-system/recipes';
import { cx } from 'styled-system/css';

export interface ButtonProps
  extends React.ComponentPropsWithoutRef<typeof ArkButton>,
          ButtonVariantProps {
  /** Optional left icon */
  leftIcon?: React.ReactNode;
  /** Optional right icon */
  rightIcon?: React.ReactNode;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      variant,
      size,
      leftIcon,
      rightIcon,
      className,
      children,
      ...props
    },
    ref
  ) => {
    return (
      <ArkButton
        ref={ref}
        className={cx(button({ variant, size }), className)}
        {...props}
      >
        {leftIcon}
        {children}
        {rightIcon}
      </ArkButton>
    );
  }
);

Button.displayName = 'Button';
```

## Barrel Export Pattern

```tsx
// src/components/Button/index.ts
export { Button } from './Button';
export type { ButtonProps } from './Button';

// src/components/index.ts
export * from './Button';
export * from './Card';
export * from './Dialog';
```

## Ark UI Component Patterns

### Simple Components (Button, Card)

```tsx
import { Button as ArkButton } from '@ark-ui/react';

// Ark Button is a simple primitive, just wrap and style
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (props, ref) => (
    <ArkButton ref={ref} className={button(props)} {...props} />
  )
);
```

### Compound Components (Dialog, Menu)

```tsx
import { Dialog as ArkDialog } from '@ark-ui/react';

// Ark Dialog is compound - has Trigger, Content, etc.
export const Dialog = {
  Root: ArkDialog.Root,
  Trigger: forwardRef<HTMLButtonElement, DialogTriggerProps>(
    ({ className, ...props }, ref) => (
      <ArkDialog.Trigger
        ref={ref}
        className={cx(dialogTrigger(), className)}
        {...props}
      />
    )
  ),
  Content: forwardRef<HTMLDivElement, DialogContentProps>(
    ({ className, ...props }, ref) => (
      <ArkDialog.Positioner>
        <ArkDialog.Content
          ref={ref}
          className={cx(dialogContent(), className)}
          {...props}
        />
      </ArkDialog.Positioner>
    )
  ),
  // ... other parts
};
```

## TypeScript Patterns

### Variant Props from Recipe

```tsx
import type { ButtonVariantProps } from 'styled-system/recipes';

// ButtonVariantProps is auto-generated by Panda CSS
// It includes: { variant?: 'filled' | 'outlined' | ... ; size?: 'sm' | 'md' | 'lg' }
```

### Polymorphic Components (as prop)

```tsx
// For components that can render as different elements
type AsProps<T extends React.ElementType> = {
  as?: T;
};

type PolymorphicProps<T extends React.ElementType, Props = {}> = 
  Props & AsProps<T> & Omit<React.ComponentPropsWithoutRef<T>, keyof Props | 'as'>;
```

### Ref Forwarding with generics

```tsx
// When the element type can change
const Card = forwardRef(
  <T extends React.ElementType = 'div'>(
    { as, ...props }: CardProps<T>,
    ref: React.ForwardedRef<Element>
  ) => {
    const Component = as || 'div';
    return <Component ref={ref} {...props} />;
  }
) as <T extends React.ElementType = 'div'>(
  props: CardProps<T> & { ref?: React.ForwardedRef<Element> }
) => React.ReactElement;
```

## Storybook Stories Pattern

```tsx
// src/components/Button/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['filled', 'outlined', 'text', 'elevated', 'tonal'],
    },
    size: {
      control: 'select',
      options: ['sm', 'md', 'lg'],
    },
  },
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Filled: Story = {
  args: {
    children: 'Button',
    variant: 'filled',
  },
};

export const Outlined: Story = {
  args: {
    children: 'Button',
    variant: 'outlined',
  },
};

// Story for all variants
export const AllVariants: Story = {
  render: () => (
    <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
      <Button variant="filled">Filled</Button>
      <Button variant="outlined">Outlined</Button>
      <Button variant="text">Text</Button>
      <Button variant="elevated">Elevated</Button>
      <Button variant="tonal">Tonal</Button>
    </div>
  ),
};
```

## Files to Reference

- `src/components/Button/Button.tsx` - Existing implementation (for reference)
- Ark UI docs: https://ark-ui.com/react/docs/overview/introduction

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/tasty-maker-studio) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-14 -->

