Join live session: Test your components in browser with Storybook Test
Docs
Storybook Docs

useOf

The default blocks supplied by Storybook do not fit all use cases, so you might want to write your own blocks.

If your own doc blocks need to interface with annotations from Storybook—that is stories, meta or components—you can use the useOf hook. Pass in a module export of a story, meta, or component and it will return its annotated form (with applied parameters, args, loaders, decorators, play function) that you can then use for anything you like. In fact, most of the existing blocks like Description and Canvas use useOf under the hood.

Here’s an example of how theuseOf hook could be used to create a custom block that displays the name of the story:

// .storybook/blocks/StoryName.jsx
 
import { useOf } from '@storybook/blocks';
 
/**
 * A block that displays the story name or title from the of prop
 * - if a story reference is passed, it renders the story name
 * - if a meta reference is passed, it renders the stories' title
 * - if nothing is passed, it defaults to the primary story
 */
export const StoryName = ({ of }) => {
  const resolvedOf = useOf(of || 'story', ['story', 'meta']);
  switch (resolvedOf.type) {
    case 'story': {
      return <h1>{resolvedOf.story.name}</h1>;
    }
    case 'meta': {
      return <h1>{resolvedOf.preparedMeta.title}</h1>;
    }
  }
  return null;
};
{/* ButtonDocs.mdx */}
 
import { Meta } from '@storybook/blocks';
import { StoryName } from '../.storybook/blocks/StoryName';
import * as ButtonStories from './Button.stories';
 
<Meta of={ButtonStories} />
 
{/* renders "Secondary" */}
<StoryName of={ButtonStories.Secondary} />
 
{/* renders "Primary" */}
<StoryName />
 
{/* renders "Button" */}
<StoryName of={ButtonStories} />

useOf

Type

(
  moduleExportOrType: ModuleExport | 'story' | 'meta' | 'component',
  validTypes?: Array<'story' | 'meta' | 'component'>
) => EnhancedResolvedModuleExportType

Parameters

moduleExportOrType

(Required)

Type: ModuleExport | 'story' | 'meta' | 'component'

Provides the story export, meta export, component export, or CSF file exports from which you get annotations.

When the custom block is in an attached doc, it’s also possible to get the primary (first) story, meta, or component by passing in a string instead. This is useful as a fallback, so the of prop can be omitted in your block. The most common pattern is using this as useOf(props.of || 'story') which will fall back to the primary story if no of prop is defined.

  • useOf('story') returns the annotated primary story in attached mode; error in unattached mode
  • useOf('meta') returns the annotated meta in attached mode; error in unattached mode
  • useOf('component') returns the annotated component specified in the meta in attached mode; error in unattached mode

validTypes

Type: Array<'story' | 'meta' | 'component'>

Optionally specify an array of valid types that your block accepts. Passing anything other than the valid type(s) will result in an error. For example, the Canvas block uses useOf(of, ['story']), which ensures it only accepts a reference to a story, not a meta or component.

Return

The return value depends on the matched type:

EnhancedResolvedModuleExportType['type'] === 'story'

Type: { type: 'story', story: PreparedStory }

For stories, annotated stories are returned as is. They are prepared, meaning that they are already merged with project and meta annotations.

EnhancedResolvedModuleExportType['type'] === 'meta'

Type: { type: 'meta', csfFile: CSFFile, preparedMeta: PreparedMeta }

For meta, the parsed CSF file is returned, along with prepared annotated meta. That is, project annotations merged with meta annotations, but no story annotations.

EnhancedResolvedModuleExportType['type'] === 'component'

Type: { type: 'component', component: Component, projectAnnotations: NormalizedProjectAnnotations }

For components, the component is returned along with project annotations; no meta or story annotations.

Note that it’s often impossible for the hook to determine if a component is passed in or any other object, so it behaves like an unknown type as well.