Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions next-themes/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ describe('setTheme', () => {
})

describe('inline script', () => {
test('should pass props to script', () => {
test('should not render script tag on client (avoids React 19 warning)', () => {
act(() => {
render(
<ThemeProvider defaultTheme="light" scriptProps={{ 'data-test': '1234' }}>
Expand All @@ -497,6 +497,9 @@ describe('inline script', () => {
)
})

expect(document.querySelector('script[data-test="1234"]')).toBeTruthy()
// ThemeScript returns null on the client to prevent the React 19 warning:
// "Scripts inside React components are never executed when rendering on the client."
// The script only renders during SSR where it runs once to prevent FOUC.
expect(document.querySelector('script[data-test="1234"]')).toBeNull()
})
})
9 changes: 8 additions & 1 deletion next-themes/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,13 @@ export const ThemeScript = React.memo(
nonce,
scriptProps
}: Omit<ThemeProviderProps, 'children'> & { defaultTheme: string }) => {
// Only render during SSR. The script executes once in the browser before
// React hydrates, preventing flash of unstyled content (FOUC). Rendering
// it again on the client would trigger a React 19 warning ("Scripts inside
// React components are never executed when rendering on the client") and
// serves no purpose since the script has already run.
if (typeof window !== 'undefined') return null

const scriptArgs = JSON.stringify([
attribute,
storageKey,
Expand All @@ -212,7 +219,7 @@ export const ThemeScript = React.memo(
<script
{...scriptProps}
suppressHydrationWarning
nonce={typeof window === 'undefined' ? nonce : ''}
nonce={nonce}
dangerouslySetInnerHTML={{ __html: `(${script.toString()})(${scriptArgs})` }}
/>
)
Expand Down
Loading