From 919eac2dde4ecab675937d7bb7ee5505ca0c7009 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 15:10:58 +0000 Subject: [PATCH 1/3] Initial plan From 0c5405ee4e5100d94d27db2721b7c9b8e255f9e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 15:26:45 +0000 Subject: [PATCH 2/3] Improve hooks error message and add documentation about mixing Storybook and React hooks Co-authored-by: valentinpalkovic <5889929+valentinpalkovic@users.noreply.github.com> --- .../src/preview-api/modules/addons/hooks.ts | 5 +++- docs/writing-stories/args.mdx | 24 +++++++++++++++++++ docs/writing-stories/decorators.mdx | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/code/core/src/preview-api/modules/addons/hooks.ts b/code/core/src/preview-api/modules/addons/hooks.ts index b0c2edaf2c9c..34ff7c2257d7 100644 --- a/code/core/src/preview-api/modules/addons/hooks.ts +++ b/code/core/src/preview-api/modules/addons/hooks.ts @@ -218,7 +218,10 @@ const areDepsEqual = (deps: any[], nextDeps: any[]) => deps.length === nextDeps.length && deps.every((dep, i) => dep === nextDeps[i]); const invalidHooksError = () => - new Error('Storybook preview hooks can only be called inside decorators and story functions.'); + new Error( + 'Storybook preview hooks can only be called inside decorators and story functions.\n\n' + + "When combining Storybook hooks (e.g. useArgs) with framework hooks (e.g. React's useState, useEffect, useRef) in the same render function, use Storybook's equivalents from 'storybook/preview-api' instead: useState, useEffect, useRef, useMemo, useCallback, useReducer." + ); function getHooksContextOrNull< TRenderer extends Renderer, diff --git a/docs/writing-stories/args.mdx b/docs/writing-stories/args.mdx index 1a69d5c2c35a..9800df918658 100644 --- a/docs/writing-stories/args.mdx +++ b/docs/writing-stories/args.mdx @@ -145,6 +145,30 @@ Args specified through the URL will extend and override any default values of ar {/* prettier-ignore-end */} + + + When using Storybook hooks (e.g., `useArgs`) in a render function, **do not** mix them with framework hooks like React's `useState`, `useEffect`, or `useRef`. React re-renders triggered by state updates (e.g., from `useState`'s setter) do not run through Storybook's hook context, which causes a `Storybook preview hooks can only be called inside decorators and story functions` error on re-render. + + Instead, use Storybook's equivalents from `storybook/preview-api`: `useState`, `useEffect`, `useRef`, `useMemo`, `useCallback`, and `useReducer`. These are designed to work correctly alongside Storybook hooks like `useArgs`. + + ```tsx + // ✅ Correct: import all hooks from storybook/preview-api + import { useArgs, useState, useEffect } from 'storybook/preview-api'; + + export const MyStory = { + render: function Render(args) { + const [{ value }, updateArgs] = useArgs(); + const [localState, setLocalState] = useState(false); + + useEffect(() => { + // side effects here + }, [value]); + + return ; + }, + }; + ``` + ## Mapping to complex arg values diff --git a/docs/writing-stories/decorators.mdx b/docs/writing-stories/decorators.mdx index 7acaf6bdfa33..2a31268544d9 100644 --- a/docs/writing-stories/decorators.mdx +++ b/docs/writing-stories/decorators.mdx @@ -62,7 +62,7 @@ The second argument to a decorator function is the **story context** which conta * `args` - the story arguments. You can use some [`args`](./args.mdx) in your decorators and drop them in the story implementation itself. * `argTypes`- Storybook's [argTypes](../api/arg-types.mdx) allow you to customize and fine-tune your stories [`args`](./args.mdx). * `globals` - Storybook-wide [globals](../essentials/toolbars-and-globals.mdx#globals). In particular you can use the [toolbars feature](../essentials/toolbars-and-globals.mdx#global-types-and-the-toolbar-annotation) to allow you to change these values using Storybook’s UI. -* `hooks` - Storybook's API hooks (e.g., useArgs). +* `hooks` - Storybook's API hooks (e.g., `useArgs`, `useGlobals`). These are available in both decorators and story render functions. When using these hooks in a render function alongside framework hooks (e.g., React's `useState`, `useEffect`), use Storybook's hook equivalents from `storybook/preview-api` instead to avoid errors on re-render. * `parameters`- the story's static metadata, most commonly used to control Storybook's behavior of features and addons. * `viewMode`- Storybook's current active window (e.g., canvas, docs). From 4bf2e5bb7f7a0518fac41af78f09eb6691fd2d0a Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Fri, 27 Feb 2026 14:45:46 +0000 Subject: [PATCH 3/3] Adjust warning callout on args --- docs/writing-stories/args.mdx | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/docs/writing-stories/args.mdx b/docs/writing-stories/args.mdx index 9800df918658..fa70545cdb38 100644 --- a/docs/writing-stories/args.mdx +++ b/docs/writing-stories/args.mdx @@ -147,27 +147,9 @@ Args specified through the URL will extend and override any default values of ar {/* prettier-ignore-end */} - When using Storybook hooks (e.g., `useArgs`) in a render function, **do not** mix them with framework hooks like React's `useState`, `useEffect`, or `useRef`. React re-renders triggered by state updates (e.g., from `useState`'s setter) do not run through Storybook's hook context, which causes a `Storybook preview hooks can only be called inside decorators and story functions` error on re-render. - Instead, use Storybook's equivalents from `storybook/preview-api`: `useState`, `useEffect`, `useRef`, `useMemo`, `useCallback`, and `useReducer`. These are designed to work correctly alongside Storybook hooks like `useArgs`. - - ```tsx - // ✅ Correct: import all hooks from storybook/preview-api - import { useArgs, useState, useEffect } from 'storybook/preview-api'; - - export const MyStory = { - render: function Render(args) { - const [{ value }, updateArgs] = useArgs(); - const [localState, setLocalState] = useState(false); - - useEffect(() => { - // side effects here - }, [value]); - - return ; - }, - }; - ``` + If you're using Storybook's hooks API in the story's render function, **do not** mix them with React's hooks such as `useState`, `useEffect`, or `useRef`. This is because side effects and re-rendering triggered by React's hooks do not run through Storybook's hook context, which can cause an error on re-render. To manage state and side effects within a story, you must use Storybook's equivalent hooks, such as `useState`, `useEffect`, and `useRef` from `storybook/preview-api`. +