Describe the bug
Passing styleNonce to e.g. <ReactQueryDevtools> no longer prevents CSP violations. The <style id="_goober"> element injected by the devtools ends up with an empty nonce, causing the browser to reject it.
Inspecting the element in Chrome DevTools and checking its properties shows nonce="undefined".
Your minimal, reproducible example
https://stackblitz.com/edit/tanstack-query-bdtw2gqj?file=src%2Findex.tsx,package.json,vite.config.ts,README.md
Steps to reproduce
| Version |
StackBlitz |
| Working |
open |
| Broken |
open |
- Create a React app with Vite and install
@tanstack/react-query and @tanstack/react-query-devtools at a version that includes goober 2.1.18 (e.g. 5.94.5).
- Add a
Content-Security-Policy header with a style-src nonce-<your-nonce> directive in vite.config.ts.
- Pass the same nonce to
<ReactQueryDevtools styleNonce="<your-nonce>" />.
- Start the dev server and open the browser console.
- Observe a CSP violation error for the
<style id="_goober"> element.
Downgrading to @tanstack/react-query-devtools@5.91.3 (goober 2.1.16) resolves the error.
Expected behavior
Passing styleNonce to <ReactQueryDevtools> should be sufficient for the devtools to work under a strict style-src CSP policy. No CSP violation errors should appear in the browser console.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
- OS: [Windows, Linux]
- Browser: [Chrome, Firefox]
Tanstack Query adapter
react-query
TanStack Query version
v5.100.14
TypeScript version
5.8.3
Additional context
Root cause
The styleNonce prop flows into setupStyleSheet(60bd38ca6, PR #6320), which pre-creates a <style id="_goober" nonce="..."> element and appends it to the document head before the devtools render.
The devtools use goober for CSS-in-JS. Goober manages a single <style id="_goober"> element and, since v2.1.17 (2d9c3085), unconditionally overwrites its nonce on every access:
el.nonce = window.__nonce__ // added in goober 2.1.17 (#612)
A follow-up fix in v2.1.18 (44334ec0) corrected the insertion order but kept the window.__nonce__ assignment.
window.__nonce__ is goober's documented CSP hook. Since setupStyleSheet never set it, goober always overwrote the nonce with undefined, either clearing it on the pre-created element or inserting a brand-new element with an empty nonce, causing the CSP violation.
Why it worked before
Prior to goober 2.1.17, goober had no nonce handling at all. It would find the pre-created #_goober element and reuse it without touching the nonce, so setupStyleSheet's approach worked correctly.
The behavior changed silently when goober added window.__nonce__ support in v2.1.17 (2d9c3085).
The goober version in this repo's lockfile stayed pinned at 2.1.16 until 8f0d83438 (PR #9935), which bumped it to 2.1.18. All releases cut after that point ship with goober 2.1.18.
Version range
@tanstack/query-devtools |
goober in lockfile |
styleNonce works? |
5.93.0 (a678f0957, #10069) last working |
2.1.16 |
yes |
5.94.4 (c613c2253, #10309) first broken |
2.1.18 |
no |
Fix
Set window.__nonce__ inside setupStyleSheet before the element is created or inserted:
export const setupStyleSheet = (nonce?: string, target?: ShadowRoot) => {
if (!nonce) return
;(window as any).__nonce__ = nonce // goober reads this on every style element access
// pre-creation kept as fallback for goober <2.1.17
const styleExists =
document.querySelector('#_goober') || target?.querySelector('#_goober')
if (styleExists) return
// ...
}
Related
Describe the bug
Passing
styleNonceto e.g.<ReactQueryDevtools>no longer prevents CSP violations. The<style id="_goober">element injected by the devtools ends up with an empty nonce, causing the browser to reject it.Inspecting the element in Chrome DevTools and checking its properties shows
nonce="undefined".Your minimal, reproducible example
https://stackblitz.com/edit/tanstack-query-bdtw2gqj?file=src%2Findex.tsx,package.json,vite.config.ts,README.md
Steps to reproduce
@tanstack/react-queryand@tanstack/react-query-devtoolsat a version that includes goober 2.1.18 (e.g.5.94.5).Content-Security-Policyheader with astyle-src nonce-<your-nonce>directive invite.config.ts.<ReactQueryDevtools styleNonce="<your-nonce>" />.<style id="_goober">element.Downgrading to
@tanstack/react-query-devtools@5.91.3(goober 2.1.16) resolves the error.Expected behavior
Passing
styleNonceto<ReactQueryDevtools>should be sufficient for the devtools to work under a strictstyle-srcCSP policy. No CSP violation errors should appear in the browser console.How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Tanstack Query adapter
react-query
TanStack Query version
v5.100.14
TypeScript version
5.8.3
Additional context
Root cause
The
styleNonceprop flows intosetupStyleSheet(60bd38ca6, PR #6320), which pre-creates a<style id="_goober" nonce="...">element and appends it to the document head before the devtools render.The devtools use goober for CSS-in-JS. Goober manages a single
<style id="_goober">element and, since v2.1.17 (2d9c3085), unconditionally overwrites its nonce on every access:A follow-up fix in v2.1.18 (
44334ec0) corrected the insertion order but kept thewindow.__nonce__assignment.window.__nonce__is goober's documented CSP hook. SincesetupStyleSheetnever set it, goober always overwrote the nonce withundefined, either clearing it on the pre-created element or inserting a brand-new element with an empty nonce, causing the CSP violation.Why it worked before
Prior to goober 2.1.17, goober had no nonce handling at all. It would find the pre-created
#_gooberelement and reuse it without touching the nonce, sosetupStyleSheet's approach worked correctly.The behavior changed silently when goober added
window.__nonce__support in v2.1.17 (2d9c3085).The goober version in this repo's lockfile stayed pinned at 2.1.16 until
8f0d83438(PR #9935), which bumped it to 2.1.18. All releases cut after that point ship with goober 2.1.18.Version range
@tanstack/query-devtoolsstyleNonceworks?a678f0957, #10069) last workingc613c2253, #10309) first brokenFix
Set
window.__nonce__insidesetupStyleSheetbefore the element is created or inserted:Related
styleNonce/setupStyleSheet:60bd38ca6(fix(query-devtools): Add styleNonce prop to devtools components #6320)2d9c3085(release)44334ec0(release)