diff --git a/.changeset/tender-years-destroy.md b/.changeset/tender-years-destroy.md
new file mode 100644
index 00000000000..2a739d665fb
--- /dev/null
+++ b/.changeset/tender-years-destroy.md
@@ -0,0 +1,16 @@
+---
+'@graphiql/plugin-doc-explorer': minor
+'@graphiql/plugin-history': minor
+'@graphiql/react': minor
+'graphiql': minor
+---
+
+- deprecate `useExplorerContext`, `useHistoryContext`, `usePrettifyEditors`, `useCopyQuery`, `useMergeQuery`, `useExecutionContext`, `usePluginContext`, `useSchemaContext`, `useStorageContext` hooks
+- fix response editor overflow on ``
+- export `GraphiQLProps` type
+- allow `children: ReactNode` for ``
+- change `ToolbarMenu` component:
+ - The `label` and `className` props were removed
+ - The `button` prop should now be a button element
+- document `useGraphiQL` and `useGraphiQLActions` hooks in `@graphiql/react` README.md
+- rename `useThemeStore` to `useTheme`
diff --git a/docs/migration/graphiql-5.0.0.md b/docs/migration/graphiql-5.0.0.md
index f2841495fb4..57fef2ce033 100644
--- a/docs/migration/graphiql-5.0.0.md
+++ b/docs/migration/graphiql-5.0.0.md
@@ -18,3 +18,25 @@
- `keyMap`. To use Vim or Emacs keybindings in Monaco, you can use community plugins. Monaco Vim: https://github.com/brijeshb42/monaco-vim. Monaco Emacs: https://github.com/aioutecism/monaco-emacs
- `readOnly`
- `validationRules`. Use custom GraphQL worker, see https://github.com/graphql/graphiql/tree/main/packages/monaco-graphql#custom-webworker-for-passing-non-static-config-to-worker.'
+
+## `@graphiql/react`
+
+The `ToolbarMenu` component has changed.
+
+- The `label` and `className` props were removed
+- The `button` prop should now be a button element
+
+ ```jsx
+
+
+
+ }
+ >
+ console.log('Clicked!')}>
+ Test
+
+
+ ```
diff --git a/packages/graphiql-plugin-doc-explorer/src/deprecated.ts b/packages/graphiql-plugin-doc-explorer/src/deprecated.ts
new file mode 100644
index 00000000000..78425a4c9a7
--- /dev/null
+++ b/packages/graphiql-plugin-doc-explorer/src/deprecated.ts
@@ -0,0 +1,13 @@
+import { useDocExplorer, useDocExplorerActions } from './context';
+
+/**
+ * @deprecated Use `useDocExplorerActions` and `useDocExplorer` hooks instead.
+ */
+export function useExplorerContext() {
+ const actions = useDocExplorerActions();
+ const explorerNavStack = useDocExplorer();
+ return {
+ ...actions,
+ explorerNavStack,
+ };
+}
diff --git a/packages/graphiql-plugin-doc-explorer/src/index.ts b/packages/graphiql-plugin-doc-explorer/src/index.ts
index 811d0603fb8..46ddae148d7 100644
--- a/packages/graphiql-plugin-doc-explorer/src/index.ts
+++ b/packages/graphiql-plugin-doc-explorer/src/index.ts
@@ -12,3 +12,4 @@ export type {
DocExplorerNavStack,
DocExplorerNavStackItem,
} from './context';
+export * from './deprecated';
diff --git a/packages/graphiql-plugin-history/src/deprecated.ts b/packages/graphiql-plugin-history/src/deprecated.ts
new file mode 100644
index 00000000000..b7c6c9e574f
--- /dev/null
+++ b/packages/graphiql-plugin-history/src/deprecated.ts
@@ -0,0 +1,10 @@
+import { useHistory, useHistoryActions } from './context';
+
+/**
+ * @deprecated Use `useHistoryActions` and `useHistory` hooks instead.
+ */
+export function useHistoryContext() {
+ const actions = useHistoryActions();
+ const items = useHistory();
+ return { ...actions, items };
+}
diff --git a/packages/graphiql-plugin-history/src/index.ts b/packages/graphiql-plugin-history/src/index.ts
index 1ea1e91b7b3..3c14c6a7154 100644
--- a/packages/graphiql-plugin-history/src/index.ts
+++ b/packages/graphiql-plugin-history/src/index.ts
@@ -12,3 +12,4 @@ export const HISTORY_PLUGIN: GraphiQLPlugin = {
export { History };
export { HistoryStore, useHistory, useHistoryActions } from './context';
+export * from './deprecated';
diff --git a/packages/graphiql-react/README.md b/packages/graphiql-react/README.md
index 586a7b38a96..51e0eeec5a2 100644
--- a/packages/graphiql-react/README.md
+++ b/packages/graphiql-react/README.md
@@ -74,31 +74,54 @@ text. If you want to use the default fonts you can load them using these files:
- `@graphiql/react/font/roboto.css`
- `@graphiql/react/font/fira-code.css`.
-You can of course use any other method to load these fonts (for example loading
+You can, of course, use any other method to load these fonts (for example, loading
them from Google Fonts).
Further details on how to use `@graphiql/react` can be found in the reference
implementation of a GraphQL IDE - Graph*i*QL - in the
[`graphiql` package](https://github.com/graphql/graphiql/blob/main/packages/graphiql/src/components/GraphiQL.tsx).
-## Available stores
+## Available Stores
-There are multiple stores that own different parts of the state that make up a
-complete GraphQL IDE. For each store there is a component
-(`Store`) that makes sure the store is initialized and managed
-properly. These components contains all the logic related to state management.
+GraphiQL uses a set of state management stores, each responsible for a specific part of the IDE's
+behavior. These stores contain all logic related to state management and can be accessed via custom
+React hooks.
-In addition, for each store, there is also a hook that
-allows you to consume its current value:
+### Core Hooks
-- `useStorage`: Provides a storage API that can be used to persist state in
- the browser (by default using `localStorage`)
-- `useEditorStore`: Manages all the editors and tabs
-- `useSchemaStore`: Fetches, validates and stores the GraphQL schema
-- `useExecutionStore`: Executes GraphQL requests
+- **`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.
+- **`useGraphiQL`**: Access the current state.
+- **`useGraphiQLActions`**: Trigger actions that mutate the state. This hook **never** rerenders.
-All context properties are documented using JSDoc comments. If you're using an
-IDE like VSCode for development these descriptions will show up in auto-complete
+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 |
+
+### Usage Example
+
+```js
+import { useGraphiQL, useGraphiQLActions, useTheme } from '@graphiql/react';
+
+// Get an action to fetch the schema
+const { introspect } = 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);
+```
+
+All store properties are documented using TSDoc comments. If you're using an
+IDE like VSCode for development, these descriptions will show up in auto-complete
tooltips. All these descriptions can also be found in the
[API Docs](https://graphiql-test.netlify.app/typedoc/modules/graphiql_react.html).
diff --git a/packages/graphiql-react/src/components/toolbar-menu/index.css b/packages/graphiql-react/src/components/toolbar-menu/index.css
deleted file mode 100644
index d49a31bc41d..00000000000
--- a/packages/graphiql-react/src/components/toolbar-menu/index.css
+++ /dev/null
@@ -1,5 +0,0 @@
-button.graphiql-toolbar-menu {
- display: block;
- height: var(--toolbar-width);
- width: var(--toolbar-width);
-}
diff --git a/packages/graphiql-react/src/components/toolbar-menu/index.tsx b/packages/graphiql-react/src/components/toolbar-menu/index.tsx
index 1f7e6fec48c..4f9cf4ceede 100644
--- a/packages/graphiql-react/src/components/toolbar-menu/index.tsx
+++ b/packages/graphiql-react/src/components/toolbar-menu/index.tsx
@@ -1,34 +1,19 @@
import type { FC, ReactNode } from 'react';
-import { cn } from '../../utility';
-import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
+import { Trigger, type DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
import { DropdownMenu } from '../dropdown-menu';
-import { Tooltip } from '../tooltip';
-import './index.css';
-interface ToolbarMenuProps {
+interface ToolbarMenuProps extends DropdownMenuProps {
button: ReactNode;
- label: string;
}
-const ToolbarMenuRoot: FC<
- ToolbarMenuProps & {
- children: ReactNode;
- className?: string;
- } & DropdownMenuProps
-> = ({ button, children, label, ...props }) => {
+const ToolbarMenuRoot: FC = ({
+ button,
+ children,
+ ...props
+}) => {
return (
-
-
- {button}
-
-
+ {button}
{children}
);
diff --git a/packages/graphiql-react/src/deprecated.ts b/packages/graphiql-react/src/deprecated.ts
new file mode 100644
index 00000000000..9faa27b9ac7
--- /dev/null
+++ b/packages/graphiql-react/src/deprecated.ts
@@ -0,0 +1,76 @@
+/* eslint-disable unicorn/prefer-export-from */
+import { useGraphiQL, useGraphiQLActions } from './components';
+import { pick } from './utility';
+import { useStorage } from './stores';
+
+/**
+ * @deprecated Use `const { prettifyEditors } = useGraphiQLActions()` instead.
+ */
+export function usePrettifyEditors() {
+ const { prettifyEditors } = useGraphiQLActions();
+ return prettifyEditors;
+}
+
+/**
+ * @deprecated Use `const { copyQuery } = useGraphiQLActions()` instead.
+ */
+export function useCopyQuery() {
+ const { copyQuery } = useGraphiQLActions();
+ return copyQuery;
+}
+
+/**
+ * @deprecated Use `const { mergeQuery } = useGraphiQLActions()` instead.
+ */
+export function useMergeQuery() {
+ const { mergeQuery } = useGraphiQLActions();
+ return mergeQuery;
+}
+
+/**
+ * @deprecated Use `useGraphiQLActions` and `useGraphiQL` hooks instead.
+ */
+export function useExecutionContext() {
+ const { run, stop } = useGraphiQLActions();
+ const values = useGraphiQL(state => ({
+ isFetching: state.isIntrospecting,
+ isSubscribed: Boolean(state.subscription),
+ operationName: state.operationName,
+ }));
+ return {
+ run,
+ stop,
+ ...values,
+ };
+}
+
+/**
+ * @deprecated Use `useGraphiQLActions` and `useGraphiQL` hooks instead.
+ */
+export function usePluginContext() {
+ const { setVisiblePlugin } = useGraphiQLActions();
+ const values = useGraphiQL(pick('plugins', 'visiblePlugin'));
+ return {
+ setVisiblePlugin,
+ ...values,
+ };
+}
+
+/**
+ * @deprecated Use `useGraphiQLActions` and `useGraphiQL` hooks instead.
+ */
+export function useSchemaContext() {
+ const { introspect } = useGraphiQLActions();
+ const values = useGraphiQL(
+ pick('fetchError', 'isFetching', 'schema', 'validationErrors'),
+ );
+ return {
+ introspect,
+ ...values,
+ };
+}
+
+/**
+ * @deprecated Use `const storage = useStorage()` instead.
+ */
+export const useStorageContext = useStorage;
diff --git a/packages/graphiql-react/src/index.ts b/packages/graphiql-react/src/index.ts
index dd3a7f039b3..bf030ad5b7f 100644
--- a/packages/graphiql-react/src/index.ts
+++ b/packages/graphiql-react/src/index.ts
@@ -1,6 +1,6 @@
import './style/root.css';
-export { useStorage, useThemeStore, type Theme } from './stores';
+export { useStorage, useTheme, type Theme } from './stores';
export * from './utility';
export type { TabsState } from './utility/tabs';
@@ -10,3 +10,4 @@ export * from './components';
export type { EditorProps, SchemaReference, SlicesWithActions } from './types';
export type { GraphiQLPlugin } from './stores/plugin';
export { KEY_MAP, formatShortcutForOS, isMacOs } from './constants';
+export * from './deprecated';
diff --git a/packages/graphiql-react/src/stores/index.ts b/packages/graphiql-react/src/stores/index.ts
index 2f4a7c1c96d..aa3401e9cf2 100644
--- a/packages/graphiql-react/src/stores/index.ts
+++ b/packages/graphiql-react/src/stores/index.ts
@@ -23,4 +23,4 @@ export {
type SchemaProps,
} from './schema';
export { storageStore, useStorage } from './storage';
-export { themeStore, useThemeStore, type Theme } from './theme';
+export { themeStore, useTheme, type Theme } from './theme';
diff --git a/packages/graphiql-react/src/stores/storage.ts b/packages/graphiql-react/src/stores/storage.ts
index 09ef0999f62..f4eafebe245 100644
--- a/packages/graphiql-react/src/stores/storage.ts
+++ b/packages/graphiql-react/src/stores/storage.ts
@@ -24,7 +24,7 @@ export const storageStore = createStore(() => ({
}));
export const StorageStore: FC = ({ storage, children }) => {
- const isMounted = useStorageStore(store => Boolean(store.storage));
+ const isMounted = useStorageStore(state => Boolean(state.storage));
useEffect(() => {
storageStore.setState({ storage: new StorageAPI(storage) });
@@ -40,4 +40,4 @@ export const StorageStore: FC = ({ storage, children }) => {
const useStorageStore = createBoundedUseStore(storageStore);
-export const useStorage = () => useStorageStore(store => store.storage);
+export const useStorage = () => useStorageStore(state => state.storage);
diff --git a/packages/graphiql-react/src/stores/theme.ts b/packages/graphiql-react/src/stores/theme.ts
index 893b22cf2ec..eb90eb669b1 100644
--- a/packages/graphiql-react/src/stores/theme.ts
+++ b/packages/graphiql-react/src/stores/theme.ts
@@ -58,7 +58,7 @@ export const ThemeStore: FC = ({
defaultTheme = null,
editorTheme = EDITOR_THEME,
}) => {
- const theme = useThemeStore(store => store.theme);
+ const theme = useTheme(state => state.theme);
useEffect(() => {
const { storage } = storageStore.getState();
@@ -104,4 +104,4 @@ function getSystemTheme() {
return systemTheme;
}
-export const useThemeStore = createBoundedUseStore(themeStore);
+export const useTheme = createBoundedUseStore(themeStore);
diff --git a/packages/graphiql-react/src/style/codemirror.css b/packages/graphiql-react/src/style/codemirror.css
index ac6add716b8..ad674f80748 100644
--- a/packages/graphiql-react/src/style/codemirror.css
+++ b/packages/graphiql-react/src/style/codemirror.css
@@ -1,10 +1,3 @@
-/* Set default background color */
-.graphiql-container .CodeMirror,
-.graphiql-container .CodeMirror-gutters {
- background: none;
- background-color: var(--editor-background, hsl(var(--color-base)));
-}
-
/* No padding around line numbers */
.graphiql-container .CodeMirror-linenumber {
padding: 0;
diff --git a/packages/graphiql-react/src/style/editor.css b/packages/graphiql-react/src/style/editor.css
index 38a4760efbe..1a3df981ff1 100644
--- a/packages/graphiql-react/src/style/editor.css
+++ b/packages/graphiql-react/src/style/editor.css
@@ -24,11 +24,6 @@
}
}
-.graphiql-response .monaco-editor {
- /* Add some padding so it doesn’t touch the tabs */
- padding-top: var(--px-16);
-}
-
/* Make hover contents be dynamic */
.monaco-hover,
.monaco-hover-content {
diff --git a/packages/graphiql/package.json b/packages/graphiql/package.json
index fdc63d33966..2f98cd197c3 100644
--- a/packages/graphiql/package.json
+++ b/packages/graphiql/package.json
@@ -26,11 +26,14 @@
"files": [
"dist",
"!dist/e2e.*",
- "!dist/workers/*"
+ "!dist/workers/*",
+ "!dist/index.umd.*",
+ "!dist/cdn.*"
],
"exports": {
"./package.json": "./package.json",
"./style.css": "./dist/style.css",
+ "./graphiql.css": "./dist/style.css",
".": "./dist/index.js",
"./setup-workers/*": {
"types": "./dist/setup-workers/*.d.ts",
diff --git a/packages/graphiql/src/index.ts b/packages/graphiql/src/index.ts
index 1b1706a46a5..1e97ea3d857 100644
--- a/packages/graphiql/src/index.ts
+++ b/packages/graphiql/src/index.ts
@@ -8,6 +8,6 @@
*/
import './style.css';
-export { GraphiQL } from './GraphiQL';
+export { GraphiQL, type GraphiQLProps } from './GraphiQL';
export { HISTORY_PLUGIN } from '@graphiql/plugin-history';
diff --git a/packages/graphiql/src/style.css b/packages/graphiql/src/style.css
index bf9c3be22a1..d12583a6905 100644
--- a/packages/graphiql/src/style.css
+++ b/packages/graphiql/src/style.css
@@ -201,7 +201,8 @@ button.graphiql-tab-add {
/* The response view */
.graphiql-container .graphiql-response {
- --editor-background: transparent;
+ /* Add some padding so it doesn’t touch the tabs */
+ padding-top: var(--px-16);
display: flex;
width: 100%;
flex-direction: column;
diff --git a/packages/graphiql/src/ui/sidebar.tsx b/packages/graphiql/src/ui/sidebar.tsx
index 33eadab1522..17ff1bfa8a6 100644
--- a/packages/graphiql/src/ui/sidebar.tsx
+++ b/packages/graphiql/src/ui/sidebar.tsx
@@ -15,7 +15,7 @@ import {
useGraphiQL,
useGraphiQLActions,
useStorage,
- useThemeStore,
+ useTheme,
VisuallyHidden,
} from '@graphiql/react';
import { ShortKeys } from './short-keys';
@@ -55,8 +55,8 @@ export const Sidebar: FC = ({
const forcedTheme =
$forcedTheme && THEMES.includes($forcedTheme) ? $forcedTheme : undefined;
- const storageContext = useStorage();
- const { theme, setTheme } = useThemeStore();
+ const storage = useStorage();
+ const { theme, setTheme } = useTheme();
const { setShouldPersistHeaders, introspect, setVisiblePlugin } =
useGraphiQLActions();
const { shouldPersistHeaders, isIntrospecting, visiblePlugin, plugins } =
@@ -99,7 +99,7 @@ export const Sidebar: FC = ({
function handleClearData() {
try {
- storageContext.clear();
+ storage.clear();
setClearStorageStatus('success');
} catch {
setClearStorageStatus('error');
diff --git a/packages/graphiql/src/ui/toolbar.tsx b/packages/graphiql/src/ui/toolbar.tsx
index 83ad1d917fb..20ebbc169cd 100644
--- a/packages/graphiql/src/ui/toolbar.tsx
+++ b/packages/graphiql/src/ui/toolbar.tsx
@@ -1,4 +1,4 @@
-import type { FC, ReactNode } from 'react';
+import type { FC, ReactElement, ReactNode } from 'react';
import {
CopyIcon,
KEY_MAP,
@@ -24,14 +24,15 @@ const DefaultToolbarRenderProps: FC<{
* Configure the UI by providing this Component as a child of GraphiQL.
*/
export const GraphiQLToolbar: FC<{
- children?: typeof DefaultToolbarRenderProps;
+ children?: typeof DefaultToolbarRenderProps | ReactNode;
}> = ({ children = DefaultToolbarRenderProps }) => {
- if (typeof children !== 'function') {
- throw new TypeError(
- 'The `GraphiQL.Toolbar` component requires a render prop function as its child.',
- );
- }
+ const isRenderProp = typeof children === 'function';
const { copyQuery, prettifyEditors, mergeQuery } = useGraphiQLActions();
+
+ if (!isRenderProp) {
+ return children as ReactElement;
+ }
+
const prettify = (