diff --git a/.changeset/famous-shirts-mate.md b/.changeset/famous-shirts-mate.md
new file mode 100644
index 00000000000..72bf718cdd4
--- /dev/null
+++ b/.changeset/famous-shirts-mate.md
@@ -0,0 +1,5 @@
+---
+'graphiql': minor
+---
+
+Add a new prop to GraphiQL component: `forcedTheme` to force the theme and hide the theme switcher.
diff --git a/.eslintrc.js b/.eslintrc.js
index f9f7e78023c..15097833cb4 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -359,6 +359,7 @@ module.exports = {
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/non-nullable-type-assertion-style': 'error',
'@typescript-eslint/consistent-type-assertions': 'error',
+ '@typescript-eslint/no-duplicate-type-constituents': 'error',
// TODO: Fix all errors for the following rules included in recommended config
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
diff --git a/packages/graphiql/cypress/e2e/theme.cy.ts b/packages/graphiql/cypress/e2e/theme.cy.ts
new file mode 100644
index 00000000000..456b99511de
--- /dev/null
+++ b/packages/graphiql/cypress/e2e/theme.cy.ts
@@ -0,0 +1,17 @@
+describe('Theme', () => {
+ it('Switches to light theme when `forcedTheme` is light', () => {
+ cy.visit('/?query={test}&forcedTheme=light');
+ cy.get('body').should('have.class', 'graphiql-light');
+ });
+
+ it('Switches to dark theme when `forcedTheme` is dark', () => {
+ cy.visit('/?query={test}&forcedTheme=dark');
+ cy.get('body').should('have.class', 'graphiql-dark');
+ });
+
+ it('Defaults to light theme when `forcedTheme` value is invalid', () => {
+ cy.visit('/?query={test}&forcedTheme=invalid');
+ cy.get('[data-value=settings]').click();
+ cy.get('.graphiql-dialog-section-title').eq(1).should('have.text', 'Theme'); // Check for the presence of the theme dialog
+ });
+});
diff --git a/packages/graphiql/resources/renderExample.js b/packages/graphiql/resources/renderExample.js
index f78403dde30..b2fc32c7d25 100644
--- a/packages/graphiql/resources/renderExample.js
+++ b/packages/graphiql/resources/renderExample.js
@@ -102,5 +102,6 @@ root.render(
shouldPersistHeaders: true,
inputValueDeprecation: GraphQLVersion.includes('15.5') ? undefined : true,
onTabChange,
+ forcedTheme: parameters.forcedTheme,
}),
);
diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx
index fb2a44522ff..5067d344515 100644
--- a/packages/graphiql/src/components/GraphiQL.tsx
+++ b/packages/graphiql/src/components/GraphiQL.tsx
@@ -14,6 +14,8 @@ import React, {
ReactElement,
useCallback,
useState,
+ useEffect,
+ useMemo,
} from 'react';
import {
@@ -168,6 +170,7 @@ export function GraphiQL({