# How to Create Custom Decorators Using makeDecorator in Storybook

> Learn to create custom decorators with Storybook's makeDecorator. This factory function streamlines adding reusable logic to your stories, enhancing your component development workflow. Get started now.

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

---

**`makeDecorator` is a factory function exported from `storybook/preview-api` that creates reusable decorators by wrapping stories with custom logic based on provided options and story parameters.**

Creating custom decorators using `makeDecorator` represents the standard pattern for Storybook addon authors who need to inject behavior, styles, or context around stories. The utility lives in the core preview API at [`code/core/src/preview-api/modules/addons/make-decorator.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/modules/addons/make-decorator.ts) and provides a structured way to handle configuration via story parameters or direct options, as documented in `docs/addons/addons-api.mdx`.

## Understanding the makeDecorator API Structure

The `makeDecorator` function follows a precise implementation pattern defined in lines 44‑66 of the source file. It accepts a configuration object and returns a decorator function that can be invoked with or without options.

### Function Signature and Parameters

The `makeDecorator` signature accepts the following properties:

- **`name`**: A string identifier for the decorator used in error messages and debugging.
- **`parameterName`**: The key used to read configuration from story parameters.
- **`wrapper`**: A function receiving `(getStory, context, { options, parameters })` that returns the rendered story.
- **`skipIfNoParametersOrOptions`** (optional): A boolean that prevents the wrapper from executing when neither options nor parameters are provided.

According to the source code in [`code/core/src/preview-api/modules/addons/make-decorator.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/modules/addons/make-decorator.ts), the returned function behaves differently based on invocation:

- **Without options**: Used directly as `addDecorator(myDecorator)`.
- **With options**: Used as `addDecorator(myDecorator({ foo: 'bar' }))`.

If a user mistakenly passes a story function directly into the returned decorator, the code throws a clear error at lines 84‑88.

## Creating a Parameter-Driven Decorator

The most common pattern involves reading configuration from story parameters. The wrapper receives parameters via the third argument object, allowing you to customize rendering based on story-specific settings.

The following example demonstrates a background decorator that reads color values from story parameters, adapted from the minimal example in [`docs/_snippets/storybook-addons-api-makedecorator.md`](https://github.com/storybookjs/storybook/blob/main/docs/_snippets/storybook-addons-api-makedecorator.md):

```tsx
// src/decorators/withBackground.ts
import { makeDecorator } from 'storybook/preview-api';

export const withBackground = makeDecorator({
  name: 'withBackground',
  parameterName: 'background',
  wrapper: (getStory, context, { parameters }) => {
    const color = parameters?.color ?? 'transparent';
    return (
      <div style={{ background: color, padding: '1rem' }}>
        {getStory(context)}
      </div>
    );
  },
});

```

Register the decorator globally in your preview configuration:

```tsx
// .storybook/preview.ts
import { withBackground } from '../src/decorators/withBackground';

export const decorators = [withBackground];

```

Individual stories can then configure the decorator via parameters:

```tsx
// stories/Button.stories.tsx
export default {
  title: 'Button',
  parameters: {
    background: { color: '#ffeb3b' },
  },
};

export const Primary = () => <Button>Click me</Button>;

```

## Advanced Configuration Options

### Conditional Execution with skipIfNoParametersOrOptions

When building utilities that should only activate when explicitly configured, set `skipIfNoParametersOrOptions: true`. This flag prevents the wrapper from running when neither options nor parameters are present, avoiding unnecessary overhead.

```tsx
// src/decorators/withLog.ts
import { makeDecorator } from 'storybook/preview-api';

export const withLog = makeDecorator({
  name: 'withLog',
  parameterName: 'log',
  skipIfNoParametersOrOptions: true,
  wrapper: (getStory, context, { options, parameters }) => {
    const prefix = options?.prefix ?? parameters?.prefix ?? '';
    console.log(`${prefix} Rendering story ${context.title}/${context.name}`);
    return getStory(context);
  },
});

```

### Disabling Decorators Per Story

The implementation automatically checks for a `disable: true` property within the story's parameters under the specified `parameterName`. If found, the wrapper is bypassed and the original story renders unchanged. To disable the logging decorator for a specific story:

```tsx
export const QuietStory = {
  parameters: {
    log: { disable: true },
  },
};

```

## Integration and Usage Patterns

`makeDecorator` is publicly exported from `storybook/preview-api` via [`code/core/src/preview-api/index.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/index.ts). Import it directly from this package rather than deep-importing from core internals.

### Global Registration vs Story-Level Parameters

Decorators can be applied globally in [`.storybook/preview.ts`](https://github.com/storybookjs/storybook/blob/main/.storybook/preview.ts) or individually to stories. Global registration applies the decorator to all stories, while parameters allow per-story customization without modifying the decorator code.

### Passing Options Directly

Instead of relying solely on story parameters, you can pass options when registering the decorator:

```tsx
// .storybook/preview.ts
import { withLog } from '../src/decorators/withLog';

export const decorators = [withLog({ prefix: '[Global] ' })];

```

When using this pattern, the `options` object in the wrapper contains these values, while `parameters` contains story-level overrides.

## Summary

- **`makeDecorator`** lives in [`code/core/src/preview-api/modules/addons/make-decorator.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/modules/addons/make-decorator.ts) and is exported from `storybook/preview-api`.
- The **`wrapper`** function receives `getStory`, `context`, and an object containing `options` and `parameters`.
- Set **`skipIfNoParametersOrOptions`** to `true` to prevent execution when no configuration is provided.
- Disable decorators per story by setting **`{ parameterName: { disable: true } }`** in story parameters.
- The API supports both global registration with options and story-level parameter configuration.

## Frequently Asked Questions

### What is the difference between options and parameters in makeDecorator?

**Options** are values passed directly to the decorator function when it is invoked, such as `myDecorator({ prefix: '[Log] ' })`, while **parameters** are values defined in the story's metadata under the specified `parameterName`. Inside the wrapper, `options` represents the decorator-level defaults and `parameters` represents story-specific overrides.

### How do I disable a custom decorator for a specific story?

To bypass a decorator for an individual story, add a `disable: true` property to the story's parameters under the decorator's configured `parameterName`. For example, if `parameterName: 'background'`, set `parameters: { background: { disable: true } }` in the story object.

### Where should I import makeDecorator from in Storybook 7+?

Import `makeDecorator` from **`storybook/preview-api`**. This is the public API surface re-exported from [`code/core/src/preview-api/index.ts`](https://github.com/storybookjs/storybook/blob/main/code/core/src/preview-api/index.ts). Avoid importing directly from the core internal file paths to ensure compatibility with future Storybook versions.

### Can I use makeDecorator with TypeScript?

Yes, `makeDecorator` is fully typed. The wrapper function receives properly typed arguments including the story context and a generic options/parameters object. TypeScript will infer types based on your configuration, though you may want to explicitly type the options interface for better IntelliSense when invoking the decorator with options.