diff --git a/packages/@react-types/shared/src/dom.d.ts b/packages/@react-types/shared/src/dom.d.ts index 101e1cbb3e1..b3ad6af8565 100644 --- a/packages/@react-types/shared/src/dom.d.ts +++ b/packages/@react-types/shared/src/dom.d.ts @@ -301,6 +301,12 @@ export interface GlobalDOMAttributes extends GlobalDOMEvents { hidden?: boolean | undefined; inert?: boolean | undefined; translate?: 'yes' | 'no' | undefined; + /** + * A React-only flag that suppresses the warning React normally logs when an element's + * server-rendered content doesn't match the client render during hydration. See [React + * docs](https://react.dev/reference/react-dom/client/hydrateRoot#suppressing-unavoidable-hydration-mismatch-errors). + */ + suppressHydrationWarning?: boolean | undefined; } /** diff --git a/packages/react-aria/src/utils/filterDOMProps.ts b/packages/react-aria/src/utils/filterDOMProps.ts index caaae807ce5..8d80c9fbe57 100644 --- a/packages/react-aria/src/utils/filterDOMProps.ts +++ b/packages/react-aria/src/utils/filterDOMProps.ts @@ -32,7 +32,14 @@ const linkPropNames = new Set([ 'referrerPolicy' ]); -const globalAttrs = new Set(['dir', 'lang', 'hidden', 'inert', 'translate']); +const globalAttrs = new Set([ + 'dir', + 'lang', + 'hidden', + 'inert', + 'translate', + 'suppressHydrationWarning' +]); const globalEvents = new Set([ 'onClick', diff --git a/packages/react-aria/test/utils/filterDOMProps.test.ts b/packages/react-aria/test/utils/filterDOMProps.test.ts new file mode 100644 index 00000000000..359553ed118 --- /dev/null +++ b/packages/react-aria/test/utils/filterDOMProps.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import {filterDOMProps} from '../../src/utils/filterDOMProps'; + +describe('filterDOMProps', () => { + it('always includes id', () => { + expect(filterDOMProps({id: 'foo'})).toEqual({id: 'foo'}); + }); + + it('strips global attributes when global is not requested', () => { + let props = {dir: 'rtl', hidden: true, suppressHydrationWarning: true}; + expect(filterDOMProps(props)).toEqual({}); + }); + + it('includes global attributes when global is requested', () => { + let props = { + dir: 'rtl', + lang: 'en', + hidden: true, + inert: true, + translate: 'no', + suppressHydrationWarning: true + } as const; + expect(filterDOMProps(props, {global: true})).toEqual(props); + }); + + it('includes suppressHydrationWarning when global is requested', () => { + expect(filterDOMProps({suppressHydrationWarning: true}, {global: true})).toEqual({ + suppressHydrationWarning: true + }); + }); + + it('strips suppressHydrationWarning when global is not requested', () => { + expect(filterDOMProps({suppressHydrationWarning: true})).toEqual({}); + }); +});