# How to Use the Storybook Controls Addon for Interactive Component Props

> Learn how to use the Storybook Controls addon to interactively edit component props in real-time. Map args and argTypes to form fields for instant UI updates.

- Repository: [Storybook/storybook](https://github.com/storybookjs/storybook)
- Tags: how-to-guide
- Published: 2026-02-27

---

**The Controls addon automatically generates a UI panel that lets you edit component props in real-time by mapping your story's `args` and `argTypes` to interactive form fields.**

The Controls addon is a core feature of the Storybook ecosystem that transforms static component documentation into live, editable playgrounds. By declaring `argTypes` with control configurations in your story files, you enable the **controls addon for interactive component props** without writing any additional UI code. This article explains the implementation details based on the `storybookjs/storybook` source code.

## What Is the Controls Addon?

The Controls addon bridges the gap between your component's prop interface and Storybook's UI. It reads metadata from your stories and renders form controls—text inputs, toggles, color pickers, and more—based on the `control` field in your `argTypes` definition.

According to the source code in [`code/core/src/controls/README.md`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/README.md), the addon consists of two main parts: the **manager** side (the UI panel) and the **preview** side (the component rendering). The manager uses `useArgTypes()` to collect metadata and renders an `ArgsTable`, while the preview uses `useArgs()` to sync state changes back to the component.

## Setting Up Interactive Props with argTypes

To enable the controls addon for interactive component props, you define `argTypes` in your story's default export. Each key in `argTypes` corresponds to a component prop, and the `control` property specifies which UI element to render.

### Declaring Control Types

The basic structure follows this pattern in your story file:

```typescript
// Button.stories.ts
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  component: Button,
  argTypes: {
    label: { control: 'text' },
    disabled: { control: 'boolean' },
    variant: {
      control: { type: 'select', options: ['primary', 'secondary', 'tertiary'] },
    },
    backgroundColor: { control: 'color' },
    count: {
      control: { type: 'range', min: 0, max: 100, step: 10 },
    },
  },
};

export default meta;

```

In [`code/core/src/controls/manager.tsx`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/manager.tsx), the `useArgTypes()` hook collects these definitions and passes them to the `ArgsTable` component. The table renders the appropriate input based on the `control.type` value.

### Supported Control Types

The Controls addon supports a comprehensive set of input types defined in the source. According to [`code/core/template/stories/controls/basics.stories.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/template/stories/controls/basics.stories.ts), the available controls include:

- **boolean** – Renders a checkbox toggle
- **text** – Standard text input field
- **number** – Numeric input with optional min/max constraints
- **range** – Slider control for numeric values
- **select** – Dropdown menu with predefined options
- **radio** – Radio button group for exclusive selection
- **color** – Color picker supporting hex, RGB, and HSL values
- **date** – Date picker input
- **file** – File upload input
- **object** – JSON editor for complex data structures
- **array** – Specialized input for array values

Each control type accepts additional configuration options. For example, the `range` control accepts `min`, `max`, and `step` parameters, while `select` accepts an `options` array.

## How Controls Work Under the Hood

Understanding the architecture helps when debugging or extending the controls addon for interactive component props. The implementation splits responsibilities between the manager (UI) and preview (canvas) environments.

### Manager Side (UI)

The manager side code lives in [`code/core/src/controls/manager.tsx`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/manager.tsx). This file registers the addon panel using the `ADDON_ID` constant defined in [`code/core/src/controls/constants.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/constants.ts) (`'storybook/controls'`).

Key implementation details from the source:

1. The `useArgTypes()` hook retrieves the `argTypes` metadata from the current story
2. The `ArgsTable` component renders the control inputs based on this metadata
3. When a user interacts with a control, the manager calls `updateArgs()` via the `useArgs()` hook
4. This triggers a message to the preview iframe to re-render the component with new args

The `ControlsPanel` component in [`code/core/src/controls/components/ControlsPanel.tsx`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/components/ControlsPanel.tsx) handles the actual rendering. It checks if the current story has argTypes with controls, and only displays the panel when relevant controls exist.

### Preview Side (Component Rendering)

The preview side is minimal by design. The file [`code/core/src/controls/preview.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/preview.ts) simply ensures that args are passed correctly to the component. As shown in the source:

```typescript
// code/core/src/controls/preview.ts (simplified)
export const parameters = {
  controls: {
    expanded: true, // Show full documentation for each arg
  },
};

```

The preview does not require explicit configuration because the Storybook framework automatically maps the `args` object to component props. When the manager sends updated args via the channel, the preview re-renders the story with the new values.

## Complete Working Example

Here is a complete, runnable example demonstrating the controls addon for interactive component props with a React component.

First, the component definition:

```tsx
// src/components/Card.tsx
import React from 'react';

interface CardProps {
  title: string;
  description?: string;
  isActive?: boolean;
  variant?: 'elevated' | 'outlined' | 'flat';
  maxWidth?: number;
  backgroundColor?: string;
}

export const Card = ({
  title,
  description = '',
  isActive = false,
  variant = 'elevated',
  maxWidth = 300,
  backgroundColor = '#ffffff',
}: CardProps) => {
  return (
    <div
      style={{
        maxWidth: `${maxWidth}px`,
        padding: '20px',
        borderRadius: variant === 'elevated' ? '8px' : '0',
        border: variant === 'outlined' ? '2px solid #ccc' : 'none',
        boxShadow: variant === 'elevated' ? '0 4px 6px rgba(0,0,0,0.1)' : 'none',
        backgroundColor,
        opacity: isActive ? 1 : 0.6,
      }}
    >
      <h3>{title}</h3>
      {description && <p>{description}</p>}
    </div>
  );
};

```

Next, the story configuration with controls:

```typescript
// src/components/Card.stories.ts
import type { Meta, StoryObj } from '@storybook/react';
import { Card } from './Card';

const meta: Meta<typeof Card> = {
  title: 'Components/Card',
  component: Card,
  parameters: {
    controls: {
      expanded: true, // Show full documentation for each control
    },
  },
  argTypes: {
    title: {
      control: 'text',
      description: 'The header text displayed on the card',
    },
    description: {
      control: 'text',
      description: 'Optional body text',
    },
    isActive: {
      control: 'boolean',
      description: 'Whether the card is in an active state',
    },
    variant: {
      control: {
        type: 'select',
        options: ['elevated', 'outlined', 'flat'],
      },
      description: 'Visual style variant',
    },
    maxWidth: {
      control: {
        type: 'range',
        min: 200,
        max: 800,
        step: 50,
      },
      description: 'Maximum width in pixels',
    },
    backgroundColor: {
      control: 'color',
      description: 'Background color of the card',
    },
  },
};

export default meta;

type Story = StoryObj<typeof Card>;

export const Default: Story = {
  args: {
    title: 'Card Title',
    description: 'This is an interactive example of the controls addon.',
    isActive: true,
    variant: 'elevated',
    maxWidth: 400,
    backgroundColor: '#f0f0f0',
  },
};

export const Outlined: Story = {
  args: {
    title: 'Outlined Card',
    variant: 'outlined',
    isActive: false,
    maxWidth: 300,
  },
};

```

When you run Storybook, the **Controls** panel appears automatically. Changing any value in the panel instantly updates the component preview without reloading the page.

## Advanced Features

Beyond basic prop editing, the controls addon for interactive component props includes several advanced capabilities for power users.

### Saving Story Changes

During development, you can persist control changes directly back to your source files. The manager side implements a "Save story" button in [`code/core/src/controls/components/SaveStory.tsx`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/components/SaveStory.tsx). When clicked, it emits `SAVE_STORY_REQUEST` events via the Storybook channel.

As implemented in [`code/core/src/controls/manager.tsx`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/manager.tsx), this feature is only available in development mode. It allows the addon to write updated `args` back to the story file, eliminating the need to manually copy values from the UI to your code.

### Custom Controls Panels

If you need specialized behavior beyond the default panel, you can build custom controls using the same underlying hooks. The `ControlsPanel` component in [`code/core/src/controls/components/ControlsPanel.tsx`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/components/ControlsPanel.tsx) demonstrates the standard pattern:

1. Retrieve current story data via `api.getCurrentStoryData()`
2. Load argTypes using `useArgTypes()`
3. Render `ArgsTable` with `updateArgs` callback
4. Call `updateArgs` when users interact with controls to trigger preview re-renders

You can import `useArgs` and `useArgTypes` from `@storybook/manager-api` to build alternative interfaces while maintaining the same reactive data flow.

## Summary

The Controls addon transforms Storybook from a static documentation tool into an interactive playground. Key takeaways include:

- Define **argTypes** with a `control` property to specify which UI element renders for each prop
- Provide default **args** in your story exports to set initial control values
- The manager side ([`code/core/src/controls/manager.tsx`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/manager.tsx)) renders the panel using `ArgsTable` and `useArgTypes()`
- The preview side ([`code/core/src/controls/preview.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/preview.ts)) automatically receives updated args and re-renders components
- Use the **Save story** feature in development to persist control changes back to source files
- Reference [`code/core/template/stories/controls/basics.stories.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/template/stories/controls/basics.stories.ts) for examples of every supported control type

## Frequently Asked Questions

### How do I disable the Controls panel for a specific story?

You can disable the Controls addon for individual stories or components by setting the `controls` parameter to `disable: true` in your story export:

```typescript
export const NoControls = {
  parameters: {
    controls: { disable: true },
  },
};

```

This prevents the Controls panel from appearing for that specific story while keeping it available elsewhere in your Storybook.

### Why aren't my controls showing up in the panel?

If controls are missing, you likely haven't defined `argTypes` with a `control` field, or your component props aren't being automatically inferred. Storybook automatically generates controls for props it can detect, but for complex types or when using JavaScript instead of TypeScript, you must manually specify `argTypes` in your default export as shown in the [`code/core/src/controls/components/ControlsPanel.tsx`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/components/ControlsPanel.tsx) implementation.

### Can I use Controls with non-React frameworks?

Yes, the Controls addon works with any framework supported by Storybook, including Vue, Angular, Svelte, and Web Components. The `argTypes` and `args` API is framework-agnostic. The preview side ([`code/core/src/controls/preview.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/controls/preview.ts)) simply passes the args object to your component, regardless of framework, while the manager side renders the same React-based UI for the controls panel.

### How do I create custom control types beyond the built-in options?

While Storybook provides built-in controls like `text`, `boolean`, and `select`, you can extend functionality using the `control` configuration's `type` property with custom matchers or by creating addon panels that use the `useArgs()` hook from `@storybook/manager-api`. For most use cases, combining the `object` control with JSON editing or using the `file` control for asset selection provides sufficient flexibility without requiring custom implementations.