Skip to content

Support non-React Storybook frameworks (Web Components, Lit, Vue, etc.) #48

Open

Description

@Rajdeepc

Problem

The addon currently couples metric collection to React via the withPerformanceMonitor decorator, which uses React Profiler and expects stories to return React elements. This means:

  1. Loading the default preview export in a non-React Storybook (e.g. @storybook/web-components-vite) causes rendering errors because the decorator wraps stories in React Profiler components.
  2. The only workaround is loading the addon via /preset (manager panel only) and writing a custom decorator from scratch that reimplements metric collection and channel communication.

We ran into this integrating the addon into Spectrum Web Components (Storybook 10 + @storybook/web-components-vite). Our workaround was a ~600-line custom decorator using web platform APIs (requestAnimationFrame, PerformanceObserver, MutationObserver, performance.memory) to feed metrics to the existing manager panel.

Suggestion

Separate the addon into framework-agnostic and framework-specific layers:

  1. Core metrics collector (framework-agnostic) -- The web platform metrics (frame timing, long tasks, CLS, event timing / INP, LoAF, memory, DOM element count) don't depend on React at all. These could live in a shared base class or module.
  2. Framework-specific adapters -- React Profiler metrics (reactRenderCount, reactMountDuration, etc.) are React-specific and should be an optional layer on top. Other frameworks could provide their own equivalents (e.g. Lit's updateComplete timing for web components, Vue's renderTracked/renderTriggered).
  3. Pluggable decorator export -- Instead of a single React-only decorator, export a factory or base decorator that frameworks can extend:
 // Possible API shape
   import { createPerformanceDecorator, WebPlatformCollector } from '@github-ui/storybook-addon-performance-panel/core';
   
   // Web components -- just web platform metrics
   export const withPerformanceMonitor = createPerformanceDecorator({
     collector: new WebPlatformCollector(),
   });
   
   // React -- web platform + React Profiler
   import { ReactProfilerCollector } from '@github-ui/storybook-addon-performance-panel/react';
   export const withPerformanceMonitor = createPerformanceDecorator({
     collector: new CompositeCollector([new WebPlatformCollector(), new ReactProfilerCollector()]),
   });
  1. Metrics schema -- The manager panel could gracefully handle missing metric groups (e.g. hide the React Profiler section when those metrics aren't present) rather than requiring every field to be populated with zeroes.

What I have built as a workaround

Our custom decorator collects these metrics using browser-native APIs:
Link to the gist: https://gist.github.com/Rajdeepc/80215eae8de1d764a176e4411e17f9cc

Metric API
FPS, frame time, dropped frames requestAnimationFrame loop
Long tasks, TBT PerformanceObserver (longtask)
Layout shifts (CLS) PerformanceObserver (layout-shift)
Input latency, INP PerformanceObserver (event)
Long animation frames (LoAF) PerformanceObserver (long-animation-frame)
Memory usage performance.memory
DOM element count MutationObserver

All of this is framework-agnostic and could be the shared foundation. Happy to contribute a PR if the architecture direction makes sense.

Activity

  1. Rajdeepc commented on Feb 23, 2026

    @Rajdeepc
    Author

    @mattcosta7 Looping in your PR here so we can consolidate the discussion and align on a refined approach within a single thread. This should help us evaluate trade-offs clearly and converge on a well-reasoned direction.

  2. mattcosta7 commented on Feb 23, 2026

    @mattcosta7
    Member

    @Rajdeepc - thanks for opening this! I agree with your proposal for the decorator factor as a method of enabling specific performance tracking per framework, and think we should work towards that approach (as well as controlling which panels are added there)

    I think #41 is a good step towards that while unblocking usage outside of react first, and a followup to implement the factory approach makes sense? Assuming that works for ya'll too

  3. mattcosta7 commented on Feb 24, 2026

    @mattcosta7
    Member

    The first steps to this shipped in v1.1.0 which has a /universal export that supports non-react environments

    We'll continue making this more pluggable for non-react framework level metrics

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions