Storybook Addon Apollo Client
This Storybook addon integrates Apollo Client into your Storybook stories, allowing developers to effectively test components that interact with GraphQL APIs. It is currently on version 10.0.0, which supports Apollo Client v3/v4 and Storybook 10+. The package has a relatively fast release cadence, often aligning its major versions with new releases of Storybook and React, requiring users to carefully manage their `storybook-addon-apollo-client` version to match their Storybook and Apollo Client installations. A key differentiator is its ability to expose GraphQL queries and responses in a dedicated Storybook addon panel, simplifying the debugging and visualization of data interactions within isolated components. It primarily achieves this by leveraging Apollo Client's `MockedProvider` and a custom Storybook decorator.
Common errors
-
globalMocks is not defined
cause Attempting to use the deprecated `globalMocks` configuration option, which was removed in v7.0.0.fixRefactor your Storybook `preview.ts` or `preview.tsx` to remove `globalMocks`. Implement the current decorator-based setup, providing mocks via `context.parameters.apolloClient.mocks` as shown in the v10.x setup guide. -
Error: Cannot find module 'storybook/internal/preview-api'
cause Incorrect or incompatible Storybook peer dependency version, or issues with Storybook's internal module resolution due to an outdated Storybook installation or a CJS/ESM mismatch.fixEnsure your Storybook dependencies (e.g., `storybook`, `@storybook/react-vite`) are updated to compatible versions (e.g., `^10.0.0` for `storybook-addon-apollo-client` v10). Verify your `main.ts` and `preview.ts` are using ESM syntax. Run `npx storybook@latest doctor` to diagnose. -
Error: Storybook's CommonJS distribution is no longer available. Please use the ESM distribution instead.
cause Using CommonJS `require()` syntax or an older Node.js version with Storybook 10+, which is ESM-only.fixUpdate your Storybook configuration files (`.storybook/main.ts`, `.storybook/preview.ts`) to use ESM `import`/`export` syntax. Ensure your Node.js version is 20.16+ or 22.19+ to support ESM.
Warnings
- breaking Versions 10.0.0, 9.0.0, and 8.0.0 introduce breaking changes, primarily related to Storybook and React version compatibility. Always consult the README's 'Versions' section for the correct `storybook-addon-apollo-client` version matching your Storybook and Apollo Client setup.
- breaking In version 7.0.0, `globalMocks` was removed in favor of a new 'addon kit' migration strategy. Using `globalMocks` will no longer work and requires a refactor of your Storybook configuration.
- breaking Storybook 10, which `storybook-addon-apollo-client` v10.0.0 supports, is ESM-only. Your `.storybook/main.ts` and other configuration files must be valid ESM. Node.js versions 20.16+ or 22.19+ are required.
- deprecated The `withApolloClient` decorator, used in versions below 6.x, has been deprecated and is no longer necessary. Continuing to use it will lead to configuration issues.
- gotcha There have been reports of Apollo queries persisting between separate stories when mocking, leading to inconsistent failures, especially for stories not using Apollo.
Install
-
npm install storybook-addon-apollo-client -
yarn add storybook-addon-apollo-client -
pnpm add storybook-addon-apollo-client
Imports
- EVENTS
const { EVENTS } = require('storybook-addon-apollo-client');import { EVENTS } from 'storybook-addon-apollo-client'; - ApolloClientAddonState
import { ApolloClientAddonState } from 'storybook-addon-apollo-client';import type { ApolloClientAddonState } from 'storybook-addon-apollo-client'; - Addon Configuration
module.exports = { addons: [ "storybook-addon-apollo-client", ], };export default { addons: [ "storybook-addon-apollo-client", ], };
Quickstart
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import { addons } from 'storybook/internal/preview-api';
import type { Preview } from '@storybook/react';
import { print } from 'graphql';
import { useEffect } from 'react';
import type { ApolloClientAddonState } from 'storybook-addon-apollo-client';
import { EVENTS } from 'storybook-addon-apollo-client';
const getMockName = (mockedResponse: MockedResponse) => {
if (mockedResponse.request.operationName) {
return mockedResponse.request.operationName;
}
const operationDefinition = mockedResponse.request.query.definitions.find(
(definition) => definition.kind === 'OperationDefinition',
);
if (operationDefinition?.name) {
return operationDefinition.name.value;
}
return `Unnamed`;
};
function stringifyOrUndefined(value: unknown) {
try {
return JSON.stringify(value, null, 2);
} catch {
return undefined;
}
}
function createResultFromMocks(mocks: MockedResponse[], activeIndex: number): ApolloClientAddonState {
const mock = mocks[activeIndex];
if (!mock) {
return {
activeIndex: -1,
options: mocks.map(getMockName),
query: undefined,
variables: undefined,
extensions: undefined,
context: undefined,
result: undefined,
error: undefined,
};
}
return {
options: mocks.map(getMockName),
activeIndex: activeIndex,
query: print(mock.request.query),
variables: stringifyOrUndefined(mock.request.variables),
extensions: stringifyOrUndefined(mock.request.extensions),
context: stringifyOrUndefined(mock.request.context),
result: stringifyOrUndefined(mock.result),
error: stringifyOrUndefined(mock.error),
};
}
const preview: Preview = {
decorators: [
(Story, context) => {
useEffect(() => {
const { mocks = [] } = context.parameters.apolloClient || {};
const channel = addons.getChannel();
const handleRequest = (activeIndex: number) => {
const state = createResultFromMocks(mocks, activeIndex);
channel.emit(EVENTS.RESULT, state);
};
handleRequest(mocks.length ? 0 : -1);
channel.on(EVENTS.REQUEST, handleRequest);
return () => {
channel.off(EVENTS.REQUEST, handleRequest);
};
}, [context.parameters.apolloClient]);
if (!context.parameters.apolloClient) {
return <Story />;
}
return (
<MockedProvider {...context.parameters.apolloClient}>
<Story />
</MockedProvider>
);
},
],
};
export default preview;