Controls

Purpose and Scope

Storybook Controls is the Essentials feature that lets a reader change a story’s args from the Storybook UI instead of editing source code for every state they want to inspect. An arg is a serializable input value passed to a story, and Controls renders suitable form inputs for those values in the addon panel. The reader problem is practical: once a component story exists, designers, developers, and testers need a safe way to explore labels, booleans, variants, colors, sizes, and nested data while seeing the component update immediately. Controls provides that feedback loop without requiring changes to the component implementation itself.

Sources: docs/essentials/controls.mdx

Controls is most useful after stories are written with args. The documentation positions args as the prerequisite because Storybook can infer a control surface from the args and from framework-specific component metadata. That gives the feature three important properties: it is convenient because common controls can be generated, portable because the same interactive stories can appear in documentation and tests, and rich because authors can customize the generated UI with argTypes. In practice, Controls sits between story authoring and documentation: it helps authors model states, then exposes those states to other readers in an interactive panel.

Sources: docs/essentials/controls.mdx

Relevant Source Files

  • docs/essentials/controls.mdx - Reader-facing documentation for the Controls Essentials page, including the feature definition, args prerequisite, argTypes customization model, and framework-specific metadata notes for generating controls.

Core Primitives

Controls depends on two story-level primitives: args and argTypes. Args are the concrete values that a story renders with, such as a button label, disabled state, or variant. ArgTypes describe those args for Storybook and addons: they can provide descriptions, constrain valid values, choose a control widget, or hide a value from the panel. The Controls page explicitly tells readers to write stories using args first, then use argTypes when Storybook’s inference needs more direction. This sequencing matters because Controls is not a separate component API; it is an addon UI over the story input model.

Sources: docs/essentials/controls.mdx

A typical story starts with a component meta object and named story exports. The meta can identify the component, which gives Storybook and framework integrations a place to infer metadata. The story then supplies args, and the preview renders those args into the component. Controls reads that same arg state and displays an editor. When a user changes a value in the panel, the active story re-renders with the updated arg. This is why Controls can support exploration, documentation, and testing with the same story source rather than duplicating examples across separate tools.

Sources: docs/essentials/controls.mdx

Configuring Control Types

The default control type is inferred when Storybook has enough information, but authors should configure controls explicitly when the UI should be narrower or more meaningful than the raw JavaScript value. For example, a string prop that represents a color can be represented as a color picker, and a string prop that only accepts variants can be represented as a select or radio group with explicit options. This is done in argTypes, where each arg name can define a control and, when needed, options or additional metadata. The result is a more accurate interactive contract for the component’s public states.

Sources: docs/essentials/controls.mdx

const meta = {
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
    size: { control: 'radio', options: ['small', 'medium', 'large'] },
    label: { control: 'text' },
  },
};
export default meta;
 
export const Primary = {
  args: {
    label: 'Button',
    size: 'medium',
  },
};

Controls can also be tuned at the page or story level with parameters. The common reader-facing uses are sorting, hiding, and narrowing the panel. Sorting determines how controls appear, such as alphabetically or with required fields first. Hiding can happen globally for a story, or per arg when a value is implementation detail rather than useful input. Include and exclude filters keep the panel focused when a component has many props. These options are especially important for documentation, where a long uncurated control list can obscure the handful of states a reader should experiment with first.

Sources: docs/essentials/controls.mdx

export const FocusedExample = {
  parameters: {
    controls: {
      sort: 'requiredFirst',
      include: ['label', 'size', 'disabled'],
    },
  },
  argTypes: {
    internalTrackingId: { control: false },
  },
};

Conditional and Curated Controls

Conditional controls let a story reveal one input only when another input makes it relevant. This keeps the Controls panel aligned with how the component actually behaves. For example, an advanced color token selector may only make sense when a customTheme arg is enabled, or a date range end field may only matter after a start date is selected. The condition belongs in argTypes, because it describes how the control UI should behave for a particular arg. Used well, conditional controls turn the panel from a raw prop list into a guided state explorer.

Sources: docs/essentials/controls.mdx

export const Themed = {
  args: {
    customTheme: false,
    themeColor: '#1ea7fd',
  },
  argTypes: {
    themeColor: {
      control: 'color',
      if: { arg: 'customTheme' },
    },
  },
};

Disabling controls is different from removing an arg from the story. A story can still pass a value that the component needs while keeping that value out of the UI. This distinction is useful for stable examples: a component might require an id, analytics callback, or generated object, but editing that field would distract users or create invalid states. Use control: false when the arg should remain documented but not editable, and use table or documentation settings when the goal is to remove it from generated docs entirely. Controls should expose meaningful component variation, not every implementation detail.

Sources: docs/essentials/controls.mdx

Framework Metadata and Inference

The Controls documentation calls out that inference is framework-aware. For Angular projects, Storybook can use Compodoc metadata when configured, including Angular inputs, outputs, properties, methods, and view or content children. The documented flow is to install the Compodoc tooling, update angular.json so Storybook’s builder can generate metadata, and import that metadata from .storybook/preview.ts|tsx. When the story meta sets the component annotation, Storybook can use that information to infer argTypes and produce matching controls. The important idea is that richer framework metadata creates better generated controls, but args remain the story-level surface users edit.

Sources: docs/essentials/controls.mdx

The same source also documents an Ember path that depends on metadata from the @storybook/ember-cli-storybook adapter. In that setup, the project updates ember-cli-build.js, restarts so storybook-docgen/index.json is generated, and imports that metadata in .storybook/preview.js. This reinforces a broader rule for contributors and framework users: Controls is generic at the UI level, but the quality of automatic argTypes depends on each renderer’s metadata pipeline. When inference is incomplete, explicit argTypes are the stable escape hatch.

Sources: docs/essentials/controls.mdx

Compact Reference

ConcernWhere to configureTypical values or shapeResult
Initial editable valuesStory args{ label: 'Save', disabled: false }Controls displays current story inputs and updates the rendered story.
Control widgetargTypes[arg].control'text', 'boolean', 'color', 'radio', 'select', object formsChooses the editor shown in the Controls panel.
Allowed choicesargTypes[arg].options['small', 'medium', 'large']Constrains selection controls to valid values.
Hide a controlargTypes[arg].controlfalseKeeps the arg available to the story while removing its editor.
Sort controlsparameters.controls.sort'alpha', 'requiredFirst', 'none'Changes the order of rows in the panel.
Filter controlsparameters.controls.include / excludestring arrays or matchersShows only the controls relevant to the example.
Conditional controlsargTypes[arg].if{ arg: 'customTheme' } and related conditionsShows a control only when another arg or global makes it relevant.

Practical Workflow

Start by writing a normal story with realistic args and verify that the component renders correctly. Then open the Controls panel and inspect what Storybook inferred. If the panel already represents the public API well, leave it alone; generated controls are part of the feature’s convenience. If a field is too broad, add argTypes with a specific control and options. If the panel is noisy, hide internal args or use include and exclude filters. If a field depends on another state, add a condition so readers see a progressive set of controls instead of invalid combinations.

Sources: docs/essentials/controls.mdx

For next steps, read the Args and Arg Types material before trying to debug Controls behavior. Args explain the data model that changes at runtime, while ArgTypes explain the annotation model that addons use to present those values. After that, review Parameters for story-level configuration such as sorting and filtering, and Autodocs if the same controls should support generated documentation pages. Controls works best when those pieces are treated as one authoring system: stories define states, argTypes describe inputs, parameters curate the experience, and the addon panel makes exploration immediate.