Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
4fc0623
upd
dimaMachina Jul 15, 2025
15df9ee
upd
dimaMachina Jul 15, 2025
795921d
upd
dimaMachina Jul 15, 2025
7a135fe
upd
dimaMachina Jul 15, 2025
7d6e757
upd
dimaMachina Jul 15, 2025
61998af
upd
dimaMachina Jul 15, 2025
094b10f
upd
dimaMachina Jul 15, 2025
32d327e
upd
dimaMachina Jul 15, 2025
9780442
upd
dimaMachina Jul 15, 2025
4f16377
upd
dimaMachina Jul 15, 2025
9537b10
upd
dimaMachina Jul 15, 2025
a013ed0
upd
dimaMachina Jul 15, 2025
5293f03
upd
dimaMachina Jul 15, 2025
51b7aa4
polish
dimaMachina Jul 15, 2025
1f7fda8
polish
dimaMachina Jul 15, 2025
d404dea
polish
dimaMachina Jul 15, 2025
1df1b85
polish
dimaMachina Jul 15, 2025
a156c53
upd
dimaMachina Jul 15, 2025
b924a44
fix unit tests
dimaMachina Jul 15, 2025
746c69d
upd
dimaMachina Jul 15, 2025
d60f8b2
upd
dimaMachina Jul 15, 2025
ba99d84
Update components.spec.tsx
dimaMachina Jul 15, 2025
e778163
Update components.spec.tsx
dimaMachina Jul 15, 2025
f39738c
Update components.spec.tsx
dimaMachina Jul 15, 2025
e0e59ca
set timeout to 6s
dimaMachina Jul 15, 2025
f4c84c1
set timeout to 7s
dimaMachina Jul 15, 2025
53ac81c
set timeout to 7s
dimaMachina Jul 15, 2025
74ba4d4
try 8
dimaMachina Jul 15, 2025
f2bb53b
15?
dimaMachina Jul 15, 2025
e3d5477
upd
dimaMachina Jul 15, 2025
199643c
upd
dimaMachina Jul 15, 2025
6fe0bbf
upd
dimaMachina Jul 15, 2025
5e952df
upd
dimaMachina Jul 15, 2025
2bfbe41
upd
dimaMachina Jul 15, 2025
deace26
upd
dimaMachina Jul 15, 2025
fc42ed9
upd
dimaMachina Jul 15, 2025
4f419b4
try use mounted to avoid hydration errors with localStorage
dimaMachina Jul 17, 2025
a754437
try use mounted to avoid hydration errors with localStorage
dimaMachina Jul 17, 2025
f502beb
try use mounted to avoid hydration errors with localStorage
dimaMachina Jul 17, 2025
e753620
prettier and timeout
dimaMachina Jul 17, 2025
c1aea3d
upd
dimaMachina Jul 17, 2025
df894ac
temporarily add `graphql-language-service` in changeset to test esm s…
dimaMachina Jul 17, 2025
a1530c3
add esm.sh file to vite `lib.entry`
dimaMachina Jul 17, 2025
43cf241
use minor semver
dimaMachina Jul 17, 2025
4cd1831
add `@graphiql/react` to peerDeps for history and doc explorer
dimaMachina Jul 17, 2025
da59a03
upd
dimaMachina Jul 17, 2025
9c6fa0c
Merge branch 'main' into fix-storage-multiple-instances
dimaMachina Jul 17, 2025
1a11186
update esm example
dimaMachina Jul 17, 2025
e10ab55
prettier
dimaMachina Jul 17, 2025
18d11ec
cleanup
dimaMachina Jul 17, 2025
b436808
better changeset
dimaMachina Jul 17, 2025
c89a2fb
Update .changeset/cold-ads-end.md
dimaMachina Jul 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/cold-ads-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@graphiql/plugin-history': minor
'@graphiql/plugin-doc-explorer': minor
'@graphiql/react': minor
'graphiql': minor
---

Ensure `storage` and `theme` store values aren't shared between GraphiQL instances. Deprecate `useTheme` and `useStorage` hooks in favour of values from `useGraphiQL` and `useGraphiQLActions` hooks

feat(`@graphiql/plugin-history`/`@graphiql/plugin-doc-explorer`): move `@graphiql/react` to `peerDependencies`
29 changes: 7 additions & 22 deletions examples/graphiql-cdn/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,19 @@
{
"imports": {
"react": "https://esm.sh/[email protected]",
"react/jsx-runtime": "https://esm.sh/[email protected]/jsx-runtime",
"react/": "https://esm.sh/[email protected]/",

"react-dom": "https://esm.sh/[email protected]",
"react-dom/client": "https://esm.sh/[email protected]/client",
"@emotion/is-prop-valid": "data:text/javascript,",
"react-dom/": "https://esm.sh/[email protected]/",

"graphiql": "https://esm.sh/graphiql?standalone&external=react,react-dom,@graphiql/react,graphql",
"graphiql/": "https://esm.sh/graphiql/",
"@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer?standalone&external=react,@graphiql/react,graphql",
"@graphiql/react": "https://esm.sh/@graphiql/react?standalone&external=react,react-dom,graphql,@emotion/is-prop-valid",
"@graphiql/react": "https://esm.sh/@graphiql/react?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid",

"@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit?standalone&external=graphql",
"graphql": "https://esm.sh/[email protected]"
"graphql": "https://esm.sh/[email protected]",
"@emotion/is-prop-valid": "data:text/javascript,"
}
}
</script>
Expand All @@ -63,23 +64,7 @@
import { GraphiQL, HISTORY_PLUGIN } from 'graphiql';
import { createGraphiQLFetcher } from '@graphiql/toolkit';
import { explorerPlugin } from '@graphiql/plugin-explorer';

import createJSONWorker from 'https://esm.sh/monaco-editor/esm/vs/language/json/json.worker.js?worker';
import createGraphQLWorker from 'https://esm.sh/monaco-graphql/esm/graphql.worker.js?worker';
import createEditorWorker from 'https://esm.sh/monaco-editor/esm/vs/editor/editor.worker.js?worker';

globalThis.MonacoEnvironment = {
getWorker(_workerId, label) {
console.info('MonacoEnvironment.getWorker', { label });
switch (label) {
case 'json':
return createJSONWorker();
case 'graphql':
return createGraphQLWorker();
}
return createEditorWorker();
},
};
import 'graphiql/setup-workers/esm.sh';

const fetcher = createGraphiQLFetcher({
url: 'https://countries.trevorblades.com',
Expand Down
12 changes: 6 additions & 6 deletions examples/graphiql-webpack/src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { explorerPlugin } from '@graphiql/plugin-explorer';
import { getSnippets } from './snippets';
import { codeExporterPlugin } from '@graphiql/plugin-code-exporter';
import { createGraphiQLFetcher } from '@graphiql/toolkit';
import { useStorage } from '@graphiql/react';
import { useGraphiQL } from '@graphiql/react';
import { serverSelectPlugin, LAST_URL_KEY } from './select-server-plugin';
import 'graphiql/setup-workers/webpack';
import './index.css';
Expand Down Expand Up @@ -81,17 +81,17 @@ function App() {
fetcher={fetcher}
shouldPersistHeaders
>
<GraphiQLStorageBound setUrl={setUrl} />
<GraphiQLContextBound setUrl={setUrl} />
</GraphiQL>
);
}

/**
* `useStorage` is a context hook that's only available within the `<GraphiQL>`
* provider tree. `<GraphiQLStorageBound>` must be rendered as a child of `<GraphiQL>`.
* `useGraphiQL` is a context hook that's only available within the `<GraphiQL>`
* provider tree. `<GraphiQLContextBound>` must be rendered as a child of `<GraphiQL>`.
*/
function GraphiQLStorageBound({ setUrl }) {
const storage = useStorage();
function GraphiQLContextBound({ setUrl }) {
const storage = useGraphiQL(state => state.storage);
const lastUrl = storage.get(LAST_URL_KEY) ?? STARTING_URL;

useEffect(() => {
Expand Down
15 changes: 10 additions & 5 deletions examples/graphiql-webpack/src/select-server-plugin.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import * as React from 'react';
import { useStorage, useSchemaStore } from '@graphiql/react';
import { useGraphiQL } from '@graphiql/react';

export const LAST_URL_KEY = 'lastURL';

export const PREV_URLS_KEY = 'previousURLs';

const SelectServer = ({ url, setUrl }) => {
const inputRef = React.useRef(null);
const storage = useStorage();
const { storage, schema, isIntrospecting, fetchError } = useGraphiQL(
state => ({
storage: state.storage,
schema: state.schema,
isIntrospecting: state.isIntrospecting,
fetchError: state.fetchError,
}),
);
const lastUrl = storage.get(LAST_URL_KEY);
const currentUrl = lastUrl ?? url;
const [inputValue, setInputValue] = React.useState(currentUrl);
Expand All @@ -16,8 +23,6 @@ const SelectServer = ({ url, setUrl }) => {
);
const [error, setError] = React.useState(null);

const { schema, isFetching, fetchError } = useSchemaStore();

const sameValue = inputValue.trim() === url;

return (
Expand Down Expand Up @@ -71,7 +76,7 @@ const SelectServer = ({ url, setUrl }) => {
</div>
</div>
)}
{isFetching ? (
{isIntrospecting ? (
<div className="select-server--schema-loading">Schema loading...</div>
) : (
schema &&
Expand Down
4 changes: 2 additions & 2 deletions packages/graphiql-plugin-doc-explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@
"test": "vitest"
},
"peerDependencies": {
"@graphiql/react": "^0.35.0-rc.0",
"graphql": "^15.5.0 || ^16.0.0 || ^17.0.0",
"react": "^18 || ^19",
"react-compiler-runtime": "19.1.0-rc.1",
"react-dom": "^18 || ^19"
},
"dependencies": {
"@graphiql/react": "^0.35.0",
"@headlessui/react": "^2.2",
"react-compiler-runtime": "19.1.0-rc.1",
"zustand": "^5"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/graphiql-plugin-history/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
"test": "vitest"
},
"peerDependencies": {
"@graphiql/react": "^0.35.0-rc.0",
"react": "^18 || ^19",
"react-compiler-runtime": "19.1.0-rc.1",
"react-dom": "^18 || ^19"
},
"dependencies": {
"@graphiql/react": "^0.35.0",
"@graphiql/toolkit": "^0.11.3",
"react-compiler-runtime": "19.1.0-rc.1",
"zustand": "^5"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ vi.mock('@graphiql/react', async () => {
variableEditor: { setValue: mockedSetVariableEditor },
headerEditor: { setValue: mockedSetHeaderEditor },
tabs: [],
storage: {
get() {},
},
};
},
};
Expand Down
13 changes: 3 additions & 10 deletions packages/graphiql-plugin-history/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import {
HistoryStore as ToolkitHistoryStore,
QueryStoreItem,
} from '@graphiql/toolkit';
import {
useGraphiQL,
pick,
useStorage,
createBoundedUseStore,
} from '@graphiql/react';
import { useGraphiQL, pick, createBoundedUseStore } from '@graphiql/react';

const historyStore = createStore<HistoryStoreType>((set, get) => ({
historyStorage: null,
Expand Down Expand Up @@ -122,12 +117,10 @@ export const HistoryStore: FC<HistoryStoreProps> = ({
maxHistoryLength = 20,
children,
}) => {
const { isFetching, tabs, activeTabIndex } = useGraphiQL(
pick('isFetching', 'tabs', 'activeTabIndex'),
const { isFetching, tabs, activeTabIndex, storage } = useGraphiQL(
pick('isFetching', 'tabs', 'activeTabIndex', 'storage'),
);
const activeTab = tabs[activeTabIndex]!;
const storage = useStorage();

const historyStorage = // eslint-disable-line react-hooks/exhaustive-deps -- false positive, code is optimized by React Compiler
new ToolkitHistoryStore(storage, maxHistoryLength);

Expand Down
1 change: 1 addition & 0 deletions packages/graphiql-plugin-history/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default defineConfig({
...Object.keys(packageJSON.dependencies),
],
output: {
// Separate chunks for all modules
preserveModules: true,
},
},
Expand Down
2 changes: 1 addition & 1 deletion packages/graphiql-plugin-history/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./setup-files.ts'],
setupFiles: ['./setup-files.ts', '../graphiql/setup-window.ts'],
alias: [
{
// Fixes Error: Failed to resolve entry for package "monaco-editor". The package may have incorrect main/module/exports specified in its package.json.
Expand Down
31 changes: 15 additions & 16 deletions packages/graphiql-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,36 +89,35 @@ React hooks.

### Core Hooks

- **`useStorage`**: Provides a storage API that can be used to persist state in the browser (by default using `localStorage`).
- **`useTheme`**: Manages the current theme and provides a method to update it.
- **`useMonaco`**: Access `monaco-editor` exports and the `monaco-graphql` instance. Designed for safe use in SSR environments.
- **`useGraphiQL`**: Access the current state.
- **`useGraphiQLActions`**: Trigger actions that mutate the state. This hook **never** rerenders.

The `useGraphiQLActions` hook **exposes all actions** across store slices.
The `useGraphiQL` hook **provides access to the following store slices**:

| Store Slice | Responsibilities |
| ----------- | -------------------------------------------------------------------------------- |
| `editor` | Manages **query**, **variables**, **headers**, and **response** editors and tabs |
| `execution` | Handles the execution of GraphQL requests |
| `plugin` | Manages plugins and the currently active plugin |
| `schema` | Fetches, validates, and stores the GraphQL schema |
| Store Slice | Responsibilities |
| ----------- | --------------------------------------------------------------------------------------------------------- |
| `storage` | Provides a storage API that can be used to persist state in the browser (by default using `localStorage`) |
| `editor` | Manages **query**, **variables**, **headers**, and **response** editors and tabs |
| `execution` | Handles the execution of GraphQL requests |
| `plugin` | Manages plugins and the currently active plugin |
| `schema` | Fetches, validates, and stores the GraphQL schema |
| `theme` | Manages the current theme and provides a method to update it |

### Usage Example

```js
import { useGraphiQL, useGraphiQLActions, useTheme } from '@graphiql/react';

// Get an action to fetch the schema
const { introspect } = useGraphiQLActions();
// Get an action to fetch the schema and an action to change theme
const { introspect, setTheme } = useGraphiQLActions();

// Get the current theme and a method to change it
const { theme, setTheme } = useTheme();

// Or use a selector to access specific parts of the state
const schema = useGraphiQL(state => state.schema);
const currentTheme = useTheme(state => state.theme);
// Use a selector to access specific parts of the state like current schema and theme
const { schema, theme } = useGraphiQL(state => ({
schema: state.schema,
theme: state.theme,
}));
```

All store properties are documented using TSDoc comments. If you're using an
Expand Down
6 changes: 2 additions & 4 deletions packages/graphiql-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"name": "@graphiql/react",
"version": "0.35.6",
"sideEffects": [
"dist/monaco-editor.js",
"dist/setup-workers/webpack.js",
"dist/setup-workers/vite.js"
"dist/setup-workers/*"
],
"repository": {
"type": "git",
Expand Down Expand Up @@ -48,6 +46,7 @@
"peerDependencies": {
"graphql": "^15.5.0 || ^16.0.0 || ^17.0.0",
"react": "^18 || ^19",
"react-compiler-runtime": "19.1.0-rc.1",
"react-dom": "^18 || ^19"
},
"dependencies": {
Expand All @@ -65,7 +64,6 @@
"monaco-editor": "^0.52.2",
"monaco-graphql": "^1.7.1",
"prettier": "^3.5.3",
"react-compiler-runtime": "19.1.0-rc.1",
"set-value": "^4.1.0",
"zustand": "^5"
},
Expand Down
12 changes: 9 additions & 3 deletions packages/graphiql-react/src/components/operation-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
getContextAtPosition,
} from 'graphql-language-service';
import { FC, useEffect, useRef } from 'react';
import { useMonaco, useStorage } from '../stores';
import { useMonaco } from '../stores';
import { useGraphiQL, useGraphiQLActions } from './provider';
import {
debounce,
Expand Down Expand Up @@ -69,6 +69,8 @@ export const OperationEditor: FC<OperationEditorProps> = ({
operationName,
externalFragments,
uriInstanceId,
storage,
monacoTheme,
} = useGraphiQL(
pick(
'initialQuery',
Expand All @@ -78,9 +80,10 @@ export const OperationEditor: FC<OperationEditorProps> = ({
'operationName',
'externalFragments',
'uriInstanceId',
'storage',
'monacoTheme',
),
);
const storage = useStorage();
const ref = useRef<HTMLDivElement>(null!);
const onClickReferenceRef = useRef<OperationEditorProps['onClickReference']>(
null!,
Expand Down Expand Up @@ -225,7 +228,10 @@ export const OperationEditor: FC<OperationEditorProps> = ({
uri: operationUri.path.replace('/', ''),
value: initialQuery,
});
const editor = createEditor(ref, { model });
const editor = createEditor(ref, {
model,
theme: monacoTheme,
});
setEditor({ queryEditor: editor });

// We don't use the generic `useChangeHandler` hook here because we want to
Expand Down
Loading
Loading