Skip to content

Commit ff2e4ca

Browse files
v6: Retire the composable GraphiQL.Toolbar and GraphiQL.Logo slots (#4425)
## Summary - Remove the composable `GraphiQL.Toolbar` and `GraphiQL.Logo` slots. #4398 already stopped rendering their v5 defaults, leaving them in an awkward half-fit spot with nowhere natural left to render; this finishes the job by removing the slots themselves. - `GraphiQL.Toolbar` is a straight removal, no deprecation period first. Custom editor actions belong on a plugin's `sessionActions` now, rendered into the tab strip alongside Prettify/Merge/Copy/Save. - `GraphiQL.Logo` is removed now that `<TopBar>` takes a new `brand` prop (any `ReactNode`), which is also exposed straight off `<GraphiQL>`. It replaces the default hexagon icon + "GraphiQL" wordmark; leave it unset to keep the default. - `GraphiQL.Footer` is untouched. - Also drops the dead `toolbar.additionalContent`/`toolbar.additionalComponent` `TypeError` guards, since they pointed at the component being removed here, and the `.graphiql-toolbar`/`.graphiql-logo` CSS those slots used. - Updates the README and the v6 migration guide with before/after snippets, and migrates the `graphiql-vite-react-router` example off both slots. Builds on #4398. ## Test plan - [x] Render `<GraphiQL>` with no extra props. The top bar shows the default hexagon icon and "GraphiQL" wordmark. - [x] Render `<GraphiQL brand="My Company">`. The top bar shows "My Company" instead, and the default wordmark is gone. - [x] Confirm the old `GraphiQL.Toolbar`/`GraphiQL.Logo` slots are gone: referencing `<GraphiQL.Toolbar>` or `<GraphiQL.Logo>` as JSX children is now a TypeScript error, and there's no runtime path that renders them. - [x] Confirm toolbar actions still work from the tab strip: Prettify, Merge Fragments, Copy, and Save each appear once and behave as before. - [x] Register a plugin with a `sessionActions` component and confirm it renders in the tab strip alongside the built-in actions. - [x] Confirm `<GraphiQL.Footer>` still renders below the response, unchanged. - [x] Run the `graphiql-vite-react-router` example and confirm the branding shows "API Explorer" and the share-query button still works from the tab strip. Refs: #4228 (builds on #4398)
1 parent c870ea3 commit ff2e4ca

15 files changed

Lines changed: 229 additions & 278 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'graphiql': major
3+
---
4+
5+
Remove the composable `GraphiQL.Toolbar` and `GraphiQL.Logo` slots. Editor actions are now contributed through a plugin's `sessionActions`, and branding is customized through the `brand` prop passed to `<GraphiQL>` (or `<TopBar>` directly). `GraphiQL.Footer` is unchanged. See the migration guide for before/after examples.

docs/migration/graphiql-6.0.0.md

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Upgrading `graphiql` to `6.0.0`
22

3-
GraphiQL 6 is a visual redesign built on a new OKLCH color system, plus a handful of mostly additive API changes: a `Transport` API that sits alongside the existing `Fetcher`, a settings dialog for theme/density/font-size, and two new first-party plugins (a visual query builder and operation collections) installed by default. The one breaking change is that the hooks deprecated in v5 are now removed, each with a drop-in replacement. See [discussion #4219](https://github.com/graphql/graphiql/discussions/4219) for the design background and to leave feedback.
3+
GraphiQL 6 is a visual redesign built on a new OKLCH color system, plus a handful of mostly additive API changes: a `Transport` API that sits alongside the existing `Fetcher`, a settings dialog for theme/density/font-size, and two new first-party plugins (a visual query builder and operation collections) installed by default. The breaking changes are the removal of the hooks deprecated in v5 and the removal of the `GraphiQL.Toolbar` / `GraphiQL.Logo` composable slots, each with a drop-in replacement. See [discussion #4219](https://github.com/graphql/graphiql/discussions/4219) for the design background and to leave feedback.
44

55
If something in your integration breaks that isn't covered here, please open an issue and we'll add it.
66

@@ -9,18 +9,19 @@ If something in your integration breaks that isn't covered here, please open an
99
1. [Overview](#overview)
1010
2. [CSS and retheming](#css-and-retheming)
1111
3. [Removed hooks](#removed-hooks)
12-
4. [New `transport` prop](#new-transport-prop)
13-
5. [New first-party plugins](#new-first-party-plugins)
14-
6. [`@graphiql/plugin-explorer` removal](#graphiqlplugin-explorer-removal)
15-
7. [Theme, density, and font-size settings](#theme-density-and-font-size-settings)
16-
8. [Considering for removal in v7](#considering-for-removal-in-v7)
17-
9. [Other notes](#other-notes)
12+
4. [Removed `GraphiQL.Toolbar` and `GraphiQL.Logo`](#removed-graphiqltoolbar-and-graphiqllogo)
13+
5. [New `transport` prop](#new-transport-prop)
14+
6. [New first-party plugins](#new-first-party-plugins)
15+
7. [`@graphiql/plugin-explorer` removal](#graphiqlplugin-explorer-removal)
16+
8. [Theme, density, and font-size settings](#theme-density-and-font-size-settings)
17+
9. [Considering for removal in v7](#considering-for-removal-in-v7)
18+
10. [Other notes](#other-notes)
1819

1920
## Overview
2021

2122
Visually, v6 introduces a new OKLCH-based design-token system, restyles every primitive component (buttons, dialogs, tabs, dropdowns, tooltips), and reworks the app chrome around a top bar, an activity rail, and a side panel. Functionally, the biggest changes are a new `Transport` API for the network layer, the active operation following your cursor instead of only updating on run, and two new default-installed plugins: a visual query builder and operation collections.
2223

23-
Most of this is additive and requires no rewrite: the old `fetcher` prop and the old CSS variables keep working. The one breaking change to watch for is the removal of the hooks that were deprecated back in v5 (`useEditorContext`, `usePluginContext`, and the rest). Each has a direct replacement, covered below. Read the sections relevant to your setup and treat the rest as optional cleanup.
24+
Most of this is additive and requires no rewrite: the old `fetcher` prop and the old CSS variables keep working. Two things to watch for: the hooks that were deprecated back in v5 (`useEditorContext`, `usePluginContext`, and the rest) are now removed, and the composable `GraphiQL.Toolbar` / `GraphiQL.Logo` children are gone in favor of a plugin `sessionActions` and a `brand` prop on the top bar. Each has a direct replacement, covered below. Read the sections relevant to your setup and treat the rest as optional cleanup.
2425

2526
## CSS and retheming
2627

@@ -195,6 +196,93 @@ const { addToHistory } = useHistoryActions();
195196

196197
See the [`@graphiql/react` README](../../packages/graphiql-react/README.md#available-stores) for the full list of available store selectors and actions.
197198

199+
## Removed `GraphiQL.Toolbar` and `GraphiQL.Logo`
200+
201+
`<GraphiQL.Toolbar>` and `<GraphiQL.Logo>` are removed. Both were compound-component children you passed to `<GraphiQL>` to customize the editor toolbar and the corner branding; in v6 those render sites don't exist anymore (the toolbar's default prettify/merge/copy actions and the corner logo were already removed from the default render in a prior v6 change), so the slots had nowhere natural left to render. `<GraphiQL.Footer>` is unchanged and still works exactly as before.
202+
203+
### `GraphiQL.Toolbar` → plugin `sessionActions`
204+
205+
Custom editor actions now live in the tab strip, next to prettify/merge/copy/save, through a plugin's `sessionActions`: an always-mounted component every registered plugin can provide, regardless of whether that plugin's pane is visible.
206+
207+
**Before:**
208+
209+
```tsx
210+
import { GraphiQL } from 'graphiql';
211+
import { ToolbarButton } from '@graphiql/react';
212+
213+
function App() {
214+
return (
215+
<GraphiQL fetcher={fetcher}>
216+
<GraphiQL.Toolbar>
217+
{({ prettify, merge, copy }) => (
218+
<>
219+
{prettify}
220+
{merge}
221+
{copy}
222+
<ToolbarButton label="My custom action" onClick={onClick} />
223+
</>
224+
)}
225+
</GraphiQL.Toolbar>
226+
</GraphiQL>
227+
);
228+
}
229+
```
230+
231+
**After:**
232+
233+
```tsx
234+
import { GraphiQL } from 'graphiql';
235+
import { HISTORY_PLUGIN } from '@graphiql/plugin-history';
236+
import { QUERY_BUILDER_PLUGIN } from '@graphiql/plugin-query-builder';
237+
import { collectionsPlugin } from '@graphiql/plugin-collections';
238+
import { ToolbarButton, type GraphiQLPlugin } from '@graphiql/react';
239+
240+
const myActionsPlugin: GraphiQLPlugin = {
241+
title: 'My Plugin',
242+
icon: MyIcon,
243+
content: MyPluginPanel,
244+
sessionActions: () => (
245+
<ToolbarButton label="My custom action" onClick={onClick} />
246+
),
247+
};
248+
249+
function App() {
250+
return (
251+
<GraphiQL
252+
fetcher={fetcher}
253+
plugins={[
254+
HISTORY_PLUGIN,
255+
QUERY_BUILDER_PLUGIN,
256+
collectionsPlugin(),
257+
myActionsPlugin,
258+
]}
259+
/>
260+
);
261+
}
262+
```
263+
264+
Prettify, merge, and copy no longer need re-wiring: they're built into the tab strip by default. Passing the `plugins` prop replaces the default-installed plugin set (as it always has), so include the ones you still want (`HISTORY_PLUGIN`, `QUERY_BUILDER_PLUGIN`, `collectionsPlugin()`) alongside your own.
265+
266+
### `GraphiQL.Logo` → the top bar's `brand` prop
267+
268+
Branding is customized through a `brand` prop passed straight to `<GraphiQL>` (or to `<TopBar>` directly if you're composing your own layout with `GraphiQLInterface`). It accepts any `ReactNode` and replaces the default hexagon icon + "GraphiQL" wordmark; leave it unset to keep the default.
269+
270+
**Before:**
271+
272+
```tsx
273+
<GraphiQL fetcher={fetcher}>
274+
<GraphiQL.Logo>My Company</GraphiQL.Logo>
275+
</GraphiQL>
276+
```
277+
278+
**After:**
279+
280+
```tsx
281+
<GraphiQL fetcher={fetcher} brand="My Company" />
282+
```
283+
284+
`brand` isn't limited to text — pass any element, like your own logo image alongside a label.
285+
198286
## New `transport` prop
199287

200288
`@graphiql/toolkit` adds `createTransport`, which takes the same options as `createGraphiQLFetcher` and produces a `Transport`. Unlike `Fetcher`, a `Transport`'s response carries the real HTTP wire data: status code, headers, body, timing, and request/response sizes. `<GraphiQL>` accepts a new `transport` prop alongside the existing `fetcher` prop; the two are mutually exclusive at the type level. The old API still works, unchanged.
Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,22 @@
11
import type { FC } from 'react';
22
import { GraphiQL } from 'graphiql';
33
import { createTransport } from '@graphiql/toolkit';
4-
import { ToolbarButton, useGraphiQL } from '@graphiql/react';
4+
import {
5+
CopyIcon,
6+
ToolbarButton,
7+
useGraphiQL,
8+
type GraphiQLPlugin,
9+
} from '@graphiql/react';
10+
import { HISTORY_PLUGIN } from '@graphiql/plugin-history';
11+
import { QUERY_BUILDER_PLUGIN } from '@graphiql/plugin-query-builder';
12+
import { collectionsPlugin } from '@graphiql/plugin-collections';
513
import 'graphiql/setup-workers/esm.sh';
614

715
const transport = createTransport({
816
url: 'https://graphql.earthdata.nasa.gov/api',
917
});
1018

11-
export const graphiql = (
12-
<GraphiQL
13-
dangerouslyAssumeSchemaIsValid
14-
defaultEditorToolsVisibility="variables"
15-
transport={transport}
16-
isHeadersEditorEnabled={false}
17-
>
18-
<GraphiQL.Logo>API Explorer</GraphiQL.Logo>
19-
<GraphiQL.Toolbar>
20-
{({ prettify, copy, merge }) => (
21-
<>
22-
{prettify}
23-
{copy}
24-
{merge}
25-
<Button />
26-
</>
27-
)}
28-
</GraphiQL.Toolbar>
29-
</GraphiQL>
30-
);
31-
32-
const Button: FC = () => {
19+
const ShareButton: FC = () => {
3320
const { queryEditor, variableEditor } = useGraphiQL(state => ({
3421
queryEditor: state.queryEditor,
3522
variableEditor: state.variableEditor,
@@ -60,3 +47,28 @@ const Button: FC = () => {
6047
</ToolbarButton>
6148
);
6249
};
50+
51+
const sharePlugin: GraphiQLPlugin = {
52+
title: 'Share Explorer Query',
53+
icon: CopyIcon,
54+
content: () => (
55+
<p>Copies a link to the current query and variables to your clipboard.</p>
56+
),
57+
sessionActions: ShareButton,
58+
};
59+
60+
export const graphiql = (
61+
<GraphiQL
62+
dangerouslyAssumeSchemaIsValid
63+
defaultEditorToolsVisibility="variables"
64+
transport={transport}
65+
isHeadersEditorEnabled={false}
66+
brand="API Explorer"
67+
plugins={[
68+
HISTORY_PLUGIN,
69+
QUERY_BUILDER_PLUGIN,
70+
collectionsPlugin(),
71+
sharePlugin,
72+
]}
73+
/>
74+
);

examples/graphiql-vite-react-router/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
"start": "react-router-serve dist/server/index.js"
1212
},
1313
"dependencies": {
14+
"@graphiql/plugin-collections": "^0.1.0-alpha.0",
15+
"@graphiql/plugin-history": "^0.4.2",
16+
"@graphiql/plugin-query-builder": "^0.1.0-alpha.0",
1417
"@graphiql/react": "^0.37.7",
1518
"@react-router/fs-routes": "^7",
1619
"@react-router/node": "^7",

packages/graphiql-react/src/components/top-bar/index.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// opt this file out so `useGraphiQL` / `useGraphiQLActions` stay live.
33
'use no memo';
44

5-
import type { FC } from 'react';
5+
import type { FC, ReactNode } from 'react';
66
import type { HttpMethod } from '@graphiql/toolkit';
77
import type { OperationDefinitionNode } from 'graphql';
88
import { useGraphiQL, useGraphiQLActions } from '../provider';
@@ -17,9 +17,14 @@ import './index.css';
1717
export type TopBarProps = {
1818
/** Version string shown in the brand pill. */
1919
version?: string;
20+
/**
21+
* Custom branding rendered in place of the default GraphiQL icon + wordmark.
22+
* @default the GraphiQL hexagon icon and "GraphiQL" wordmark
23+
*/
24+
brand?: ReactNode;
2025
};
2126

22-
export const TopBar: FC<TopBarProps> = ({ version }) => {
27+
export const TopBar: FC<TopBarProps> = ({ version, brand }) => {
2328
const { run, setTransportMethod, setOperationName } = useGraphiQLActions();
2429
const isFetching = useGraphiQL(state => state.isFetching);
2530
const transport = useGraphiQL(state => state.transport);
@@ -43,6 +48,7 @@ export const TopBar: FC<TopBarProps> = ({ version }) => {
4348
return (
4449
<TopBarView
4550
version={version}
51+
brand={brand}
4652
isFetching={isFetching}
4753
url={url}
4854
method={method}
@@ -61,6 +67,7 @@ export const TopBar: FC<TopBarProps> = ({ version }) => {
6167

6268
export type TopBarViewProps = {
6369
version?: string;
70+
brand?: ReactNode;
6471
isFetching: boolean;
6572
url: string;
6673
method: HttpMethod;
@@ -80,6 +87,7 @@ export type TopBarViewProps = {
8087

8188
export const TopBarView: FC<TopBarViewProps> = ({
8289
version,
90+
brand,
8391
isFetching,
8492
url,
8593
method,
@@ -203,8 +211,15 @@ export const TopBarView: FC<TopBarViewProps> = ({
203211
return (
204212
<header className="graphiql-top-bar" role="banner">
205213
<div className="graphiql-top-bar-brand">
206-
<GraphQLLogoIcon className="graphiql-top-bar-logo" aria-hidden="true" />
207-
<span className="graphiql-top-bar-wordmark">GraphiQL</span>
214+
{brand ?? (
215+
<>
216+
<GraphQLLogoIcon
217+
className="graphiql-top-bar-logo"
218+
aria-hidden="true"
219+
/>
220+
<span className="graphiql-top-bar-wordmark">GraphiQL</span>
221+
</>
222+
)}
208223
{version && <span className="graphiql-top-bar-version">{version}</span>}
209224
</div>
210225

packages/graphiql-react/src/components/top-bar/top-bar.stories.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,23 @@ export const NoVersion: Story = {
100100
],
101101
};
102102

103+
/** Custom branding in place of the default hexagon icon + "GraphiQL" wordmark. */
104+
export const CustomBrand: Story = {
105+
args: {
106+
version: 'v6.0.0-alpha.1',
107+
brand: <span style={{ fontWeight: 600 }}>My Company GraphQL Explorer</span>,
108+
},
109+
decorators: [
110+
Story => (
111+
<Tooltip.Provider>
112+
<GraphiQLProvider transport={postOnlyTransport}>
113+
<Story />
114+
</GraphiQLProvider>
115+
</Tooltip.Provider>
116+
),
117+
],
118+
};
119+
103120
/** GET selected with a mutation in the editor: Run disabled, method toggle highlighted. */
104121
export const MutationBlockedOverGet: Story = {
105122
render: () => (

packages/graphiql-react/src/components/top-bar/top-bar.test.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ describe('TopBarView', () => {
4141
expect(screen.getByText('GraphiQL')).toBeInTheDocument();
4242
});
4343

44+
it('renders custom branding in place of the default wordmark', () => {
45+
const { queryByText } = render(
46+
<TopBarView {...DEFAULTS} brand={<span>My Company</span>} />,
47+
);
48+
expect(queryByText('My Company')).toBeInTheDocument();
49+
expect(queryByText('GraphiQL')).not.toBeInTheDocument();
50+
});
51+
4452
it('renders the version pill when provided', () => {
4553
render(<TopBarView {...DEFAULTS} version="v6.0.0-alpha.1" />);
4654
expect(screen.getByText('v6.0.0-alpha.1')).toBeInTheDocument();

packages/graphiql/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,15 @@ For props documentation, see the
132132
Parts of the UI can be customized by passing children to the `GraphiQL` or the
133133
`GraphiQLInterface` component.
134134

135-
- `<GraphiQL.Logo>`: Replace the GraphiQL logo with your own.
135+
- `<GraphiQL.Footer>`: Add a custom footer shown below the response editor.
136136

137-
- `<GraphiQL.Toolbar>`: Add a custom toolbar below the execution button. Pass
138-
the empty `<GraphiQL.Toolbar />` if an empty toolbar is desired. Use the
139-
components provided by `@graphiql/react` to create toolbar buttons with proper
140-
styles.
137+
Branding and toolbar customization moved off the children API in `graphiql@6`:
141138

142-
- `<GraphiQL.Footer>`: Add a custom footer shown below the response editor.
139+
- Replace the top bar's default hexagon icon + "GraphiQL" wordmark with the
140+
`brand` prop, e.g. `<GraphiQL brand="My Company" />`.
141+
- Add custom toolbar buttons through a plugin's `sessionActions`, which renders
142+
into the tab strip alongside prettify/merge/copy/save. See the
143+
[v6 migration guide](../../docs/migration/graphiql-6.0.0.md) for details.
143144

144145
### Plugins
145146

0 commit comments

Comments
 (0)