Technical

Building an AI-Friendly React Component Library

Learn how to structure React components for AI code generation—clear props, semantic tokens, predictable patterns, and machine-readable documentation.

FramingUI Team10 min read

The AI Code Generation Problem

Your team uses AI coding tools—Claude, Cursor, GitHub Copilot. You ask for a new feature. AI generates a form, a modal, a dashboard. The code works, but it does not look right.

Buttons have the wrong padding. Colors do not match your palette. Spacing is inconsistent. The AI did not use your component library—it invented new components from scratch.

This is not the AI's fault. Your component library is optimized for humans, not machines.

AI cannot read Storybook. It cannot infer conventions from Figma. It reads TypeScript types and code structure. If your components are not structured for machine consumption, AI defaults to generic patterns learned from public repositories.

Here is how to build a React component library that AI understands—so generated code respects your design system from day one.

Principle 1: Semantic Tokens Over Magic Numbers

AI sees this:

<button style={{ padding: '12px 24px', background: '#3b82f6', borderRadius: '8px' }}>
  Submit
</button>

It learns: "Buttons use 12px/24px padding, blue-ish backgrounds, 8px border radius."

Next time it generates a button:

<button style={{ padding: '14px 28px', background: '#4f9af7', borderRadius: '6px' }}>
  Cancel
</button>

Close, but wrong. Your spacing is 12px/24px, not 14px/28px. Your blue is #3b82f6, not #4f9af7. Each generation drifts further from your system.

Solution: Export Semantic Tokens

Define tokens in code, not just design tools:

// tokens.ts
export const tokens = {
  spacing: {
    xs: '4px',
    sm: '8px',
    md: '12px',
    lg: '24px',
    xl: '32px',
  },
  color: {
    primary: {
      solid: '#3b82f6',
      hover: '#2563eb',
      text: '#1e40af',
    },
    neutral: {
      solid: '#374151',
      muted: '#6b7280',
    },
  },
  radius: {
    sm: '4px',
    md: '8px',
    lg: '12px',
  },
};

Use them in components:

import { tokens } from './tokens';

export const Button = ({ children }: { children: React.ReactNode }) => (
  <button
    style={{
      padding: `${tokens.spacing.md} ${tokens.spacing.lg}`,
      background: tokens.color.primary.solid,
      borderRadius: tokens.radius.md,
    }}
  >
    {children}
  </button>
);

Result: AI learns the pattern. Next time it generates a button, it imports tokens and uses them:

import { tokens } from './tokens';

<button
  style={{
    padding: `${tokens.spacing.md} ${tokens.spacing.lg}`,
    background: tokens.color.primary.solid,
    borderRadius: tokens.radius.md,
  }}
>
  Cancel
</button>

Consistent. No drift.

Principle 2: Predictable Prop Naming

AI reads prop names to understand how components work. Inconsistent names confuse it.

Bad (inconsistent):

<TextInput label="Email" val={email} change={setEmail} />
<NumberInput text="Age" value={age} onChange={setAge} />
<Checkbox caption="Subscribe" checked={subscribed} onCheck={setSubscribed} />

AI cannot predict the pattern. Is the value prop called val, value, or something else? Is the change handler change, onChange, or onCheck?

Good (consistent):

<TextField label="Email" value={email} onValueChange={setEmail} />
<NumberField label="Age" value={age} onValueChange={setAge} />
<Checkbox label="Subscribe" checked={subscribed} onCheckedChange={setSubscribed} />

Pattern:

  • Text inputs use value + onValueChange
  • Checkboxes use checked + onCheckedChange
  • All use label (not text, caption, or title)

AI learns this. When it needs a new input, it follows the same convention.

Standardize Across Component Categories

Component TypeValue PropChange Handler
Text inputsvalueonValueChange
Checkboxes/SwitchescheckedonCheckedChange
Selects/RadiosvalueonValueChange
SlidersvalueonValueChange

Consistency makes AI-generated code predictable.

Principle 3: Explicit Variants Over Arbitrary Props

AI struggles with open-ended props. It does not know which values are valid.

Bad (arbitrary):

<Button color="blue" size="medium" style="filled" />

AI will generate:

<Button color="lightblue" size="large" style="outline" />

Close, but your system uses primary, not lightblue. lg, not large. ghost, not outline.

Good (explicit TypeScript enums):

type ButtonProps = {
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
};

export const Button = ({ variant = 'primary', size = 'md' }: ButtonProps) => {
  // ...
};

Result: AI reads the TypeScript types. It knows the valid options:

<Button variant="primary" size="lg" />

No guessing. No invalid values.

Use String Literal Types, Not string

// ❌ Bad: AI doesn't know what's valid
type ButtonProps = {
  variant: string;
};

// ✅ Good: AI knows the exact options
type ButtonProps = {
  variant: 'primary' | 'secondary' | 'ghost';
};

Principle 4: JSDoc for Machine-Readable Documentation

AI does not browse Storybook. It reads code. Add JSDoc comments directly to components:

/**
 * A primary action button.
 * 
 * @param variant - Visual style: `primary` (default), `secondary`, `ghost`
 * @param size - Button size: `sm`, `md` (default), `lg`
 * @param children - Button label
 * 
 * @example
 * <Button variant="primary" size="lg">
 *   Submit Form
 * </Button>
 */
export const Button = ({
  variant = 'primary',
  size = 'md',
  children,
}: ButtonProps) => {
  // ...
};

AI reads this. It knows:

  • Valid variant and size values
  • What children represents (button label)
  • How to use it (the example)

When generating code, AI references the JSDoc and follows the pattern.

Principle 5: Composition Over Configuration

Complex props are hard for AI to predict. Simple composition is easier.

Bad (configuration-heavy):

<Card 
  title="User Profile" 
  subtitle="Edit your information"
  footer={<Button>Save</Button>}
  padding="large"
  shadow="medium"
/>

AI has to guess prop names: title or heading? footer or actions? padding or spacing?

Good (composition):

<Card>
  <CardHeader>
    <CardTitle>User Profile</CardTitle>
    <CardSubtitle>Edit your information</CardSubtitle>
  </CardHeader>
  <CardContent>
    {/* form fields */}
  </CardContent>
  <CardFooter>
    <Button>Save</Button>
  </CardFooter>
</Card>

Why it is better for AI:

  • Clear component hierarchy (Header → Title, Subtitle)
  • No ambiguous prop names
  • Easier to extend (add CardActions, CardImage, etc.)

AI sees this structure once and replicates it consistently.

Principle 6: Single Source of Truth for Tokens

Do not scatter token definitions across multiple files:

Bad (scattered):

src/
  styles/
    colors.css      ← Some colors here
  theme/
    spacing.ts      ← Spacing here
  constants/
    typography.js   ← Typography here

AI does not know where to look. It defaults to hardcoded values.

Good (single source):

src/
  design-system/
    tokens.ts       ← All tokens here
    components/
      Button.tsx    ← Imports from tokens.ts
      TextField.tsx ← Imports from tokens.ts

In your README or AI config:

# Design System

All design tokens are in `src/design-system/tokens.ts`.

When generating UI code, always import tokens from this file.

Claude, Cursor, and other AI tools can read this and prioritize the correct file.

Principle 7: Avoid Styled-Components for AI Generation

Styled-components are great for humans. Not for AI.

Bad (styled-components):

const StyledButton = styled.button`
  padding: ${props => props.size === 'lg' ? '16px 32px' : '12px 24px'};
  background: ${props => props.variant === 'primary' ? '#3b82f6' : '#6b7280'};
  border-radius: 8px;
`;

AI cannot easily extract token values from template strings. It sees inline styles and copies them inconsistently.

Good (inline styles or utility classes):

<button
  style={{
    padding: size === 'lg' ? `${tokens.spacing.lg} ${tokens.spacing.xl}` : `${tokens.spacing.md} ${tokens.spacing.lg}`,
    background: variant === 'primary' ? tokens.color.primary.solid : tokens.color.neutral.solid,
    borderRadius: tokens.radius.md,
  }}
>
  {children}
</button>

Or use utility classes (Tailwind, custom CSS):

<button className={cn(
  'rounded-md',
  size === 'lg' ? 'px-8 py-4' : 'px-6 py-3',
  variant === 'primary' ? 'bg-primary' : 'bg-neutral'
)}>
  {children}
</button>

AI can parse this. It understands the logic. It replicates it consistently.

Principle 8: Document Component Relationships

AI needs to know how components relate.

Example: Form Field Pattern

/**
 * Form field wrapper. Includes label, input, and error message.
 * 
 * Always use this pattern for form inputs:
 * 
 * @example
 * <Field label="Email" error={errors.email}>
 *   <TextField value={email} onValueChange={setEmail} />
 * </Field>
 */
export const Field = ({ label, error, children }: FieldProps) => (
  <div className="field">
    <label>{label}</label>
    {children}
    {error && <span className="error">{error}</span>}
  </div>
);

Now AI knows: "Form inputs always wrap in <Field>."

When it generates a form:

<Field label="Name" error={errors.name}>
  <TextField value={name} onValueChange={setName} />
</Field>

<Field label="Email" error={errors.email}>
  <TextField value={email} onValueChange={setEmail} />
</Field>

Consistent. No random label placements or error message styles.

Principle 9: Export a Component Index

AI does not explore your folder structure. Give it an entry point:

// src/design-system/index.ts
export { Button } from './components/Button';
export { TextField } from './components/TextField';
export { Card, CardHeader, CardTitle, CardContent } from './components/Card';
export { tokens } from './tokens';

In your README:

# Component Library

Import components from `@/design-system`:

\`\`\`tsx
import { Button, TextField, tokens } from '@/design-system';
\`\`\`

AI reads this. It knows where to import from. No more import Button from '../../components/ui/button'.

Principle 10: Test AI Generation with Real Prompts

Build your component library, then test it:

Prompt AI:

"Create a user registration form with email, password, and submit button. Use our design system."

Check:

  • Does it import from the correct path?
  • Does it use tokens, not hardcoded values?
  • Are prop names correct?
  • Is the structure consistent with your existing components?

If AI generates incorrect code, identify the pattern it missed and improve your component structure or documentation.

Real-World Example: Button Component

Here is an AI-friendly Button component:

import { tokens } from '../tokens';

/**
 * Button component for primary actions.
 * 
 * @param variant - `primary` (default), `secondary`, `ghost`
 * @param size - `sm`, `md` (default), `lg`
 * @param children - Button label
 * 
 * @example
 * <Button variant="primary" size="lg">
 *   Submit Form
 * </Button>
 */
export const Button = ({
  variant = 'primary',
  size = 'md',
  children,
  ...props
}: ButtonProps) => {
  const styles = {
    padding:
      size === 'sm' ? `${tokens.spacing.sm} ${tokens.spacing.md}` :
      size === 'lg' ? `${tokens.spacing.lg} ${tokens.spacing.xl}` :
      `${tokens.spacing.md} ${tokens.spacing.lg}`,
    background:
      variant === 'primary' ? tokens.color.primary.solid :
      variant === 'secondary' ? tokens.color.neutral.solid :
      'transparent',
    borderRadius: tokens.radius.md,
  };

  return (
    <button style={styles} {...props}>
      {children}
    </button>
  );
};

type ButtonProps = {
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

Why it is AI-friendly:

  • ✅ Uses semantic tokens (tokens.spacing.md, not 12px)
  • ✅ Explicit variant types ('primary' | 'secondary' | 'ghost')
  • ✅ JSDoc with examples
  • ✅ Predictable prop names (variant, size, children)
  • ✅ Inline styles (AI can parse easily)

Checklist: Is Your Component Library AI-Friendly?

  • ✅ Design tokens exported to code (not just Figma)
  • ✅ Consistent prop naming across all components
  • ✅ TypeScript enums for variants (no arbitrary strings)
  • ✅ JSDoc comments with examples
  • ✅ Composition over configuration
  • ✅ Single source of truth for tokens
  • ✅ Avoid styled-components (use inline styles or utility classes)
  • ✅ Document component relationships (e.g., Field + TextField)
  • ✅ Export all components from a single index file
  • ✅ Test with real AI prompts

Conclusion

AI code generation is fast. But fast, inconsistent code creates design debt.

Your component library needs to be structured for machines, not just humans:

  • Semantic tokens, not magic numbers
  • Explicit types, not open-ended strings
  • JSDoc documentation, not just Storybook
  • Composition, not complex props
  • Single source of truth

Make these changes, and AI-generated code will respect your design system from day one. You get velocity without chaos.

Your component library is not just for developers. It is for the AI tools those developers use. Build for both, and you get consistent UI at AI speed.


Want an AI-ready component library out of the box?

FramingUI is built for AI code generation—semantic tokens, predictable component APIs, JSDoc everywhere.

Explore FramingUI

Ready to build with FramingUI?

Build consistent UI with AI-ready design tokens. No more hallucinated colors or spacing.

Try FramingUI
Share

Related Posts