Skip to content
storybook-addon-performance-panel

Getting started

Installation

npm install @github-ui/storybook-addon-performance-panel

React projects

Register the addon in main.ts and compose the decorator in preview.ts.

// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite'

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(ts|tsx)'],
  framework: '@storybook/react-vite',
  addons: ['@github-ui/storybook-addon-performance-panel'],
}

export default config
// .storybook/preview.ts
import addonPerformancePanel from '@github-ui/storybook-addon-performance-panel'
import { definePreview } from '@storybook/react-vite'

const preview = definePreview({
  addons: [addonPerformancePanel()],
})

export default preview

Other frameworks

Use the ./universal subpath for HTML, Vue, Svelte, Web Components, or any non-React framework. It collects all browser-level metrics and hides the React Performance section.

// .storybook/main.ts
import type { StorybookConfig } from '@storybook/html-vite'

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(ts|tsx)'],
  framework: '@storybook/html-vite',
  addons: ['@github-ui/storybook-addon-performance-panel/universal'],
}

export default config
// .storybook/preview.ts
import addonPerformancePanel from '@github-ui/storybook-addon-performance-panel/universal'
import { definePreview } from '@storybook/html-vite'

const preview = definePreview({
  addons: [addonPerformancePanel()],
})

export default preview

Writing stories

The performance decorator wraps every story automatically — no changes needed to your stories. Here's an example using the CSF factory API:

// Button.stories.tsx
import preview from '../.storybook/preview'

function Button({ label = 'Click me' }: { label?: string }) {
  return <button>{label}</button>
}

const meta = preview.meta({
  title: 'Components/Button',
  component: Button,
})
export default meta

export const Default = meta.story({})
export const WithLabel = meta.story({ args: { label: 'Submit' } })

React profiling in production

Production React builds strip the Profiler API by default. To keep it in deployed Storybook builds, alias react-dom/client to react-dom/profiling:

// .storybook/main.ts
const config: StorybookConfig = {
  // ...
  viteFinal(config) {
    config.resolve ??= {}
    config.resolve.alias ??= {}
    ;(config.resolve.alias as Record<string, string>)['react-dom/client'] =
      'react-dom/profiling'
    return config
  },
}

Element timing

Track specific elements by adding the elementtiming attribute:

<img src="/hero.jpg" elementtiming="hero-image" />
<div elementtiming="main-content">Important content</div>

Each tracked element appears in the Element Timing panel section with its render time.

View mode behavior

  • Story (Canvas) view — full performance metrics are collected and displayed.
  • Docs view — a message explains that metrics are optimized for Canvas view. Docs mode renders stories in iframes, which affects timing accuracy.
Navigation