diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 36b1c8fcc55d00..594bb47e187d51 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -23,11 +23,13 @@
- `Composite`: add stable version of the component ([#63564](https://github.com/WordPress/gutenberg/pull/63564)).
- `Composite`: add `Hover` and `Typeahead` subcomponents ([#64399](https://github.com/WordPress/gutenberg/pull/64399)).
-- `Composite`: export `useCompositeStore, add focus-related props to `Composite`and`Composite.Item` subcomponents ([#64450](https://github.com/WordPress/gutenberg/pull/64450)).
+- `Composite`: export `useCompositeStore`, add focus-related props to `Composite`and`Composite.Item` subcomponents ([#64450](https://github.com/WordPress/gutenberg/pull/64450)).
+- `Composite`: add `Context` subcomponent ([#64493](https://github.com/WordPress/gutenberg/pull/64493)).
### Enhancements
- `Composite`: improve Storybook examples and add interactive controls ([#64397](https://github.com/WordPress/gutenberg/pull/64397)).
+- `Composite`: use internal context to forward the composite store to sub-components ([#64493](https://github.com/WordPress/gutenberg/pull/64493)).
- `QueryControls`: Default to new 40px size ([#64457](https://github.com/WordPress/gutenberg/pull/64457)).
- `TimePicker`: add `hideLabelFromVision` prop ([#64267](https://github.com/WordPress/gutenberg/pull/64267)).
- `DropdownMenuV2`: adopt elevation scale ([#64432](https://github.com/WordPress/gutenberg/pull/64432)).
diff --git a/packages/components/src/composite/README.md b/packages/components/src/composite/README.md
index 3670e31b01e9df..76e345b16d13fa 100644
--- a/packages/components/src/composite/README.md
+++ b/packages/components/src/composite/README.md
@@ -315,3 +315,7 @@ Allows the component to be rendered as a different HTML element or React compone
The contents of the component.
- Required: no
+
+### `Composite.Context`
+
+The React context used by the composite components. It can be used by to access the composite store, and to forward the context when composite sub-components are rendered across portals (ie. `SlotFill` components) that would not otherwise forward the context to the `Fill` children.
diff --git a/packages/components/src/composite/context.ts b/packages/components/src/composite/context.ts
new file mode 100644
index 00000000000000..69a052c5bfba19
--- /dev/null
+++ b/packages/components/src/composite/context.ts
@@ -0,0 +1,14 @@
+/**
+ * WordPress dependencies
+ */
+import { createContext, useContext } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import type { CompositeContextProps } from './types';
+
+export const CompositeContext =
+ createContext< CompositeContextProps >( undefined );
+
+export const useCompositeContext = () => useContext( CompositeContext );
diff --git a/packages/components/src/composite/index.tsx b/packages/components/src/composite/index.tsx
index f5d92330cada3c..0bfcec2bf76600 100644
--- a/packages/components/src/composite/index.tsx
+++ b/packages/components/src/composite/index.tsx
@@ -16,12 +16,13 @@ import * as Ariakit from '@ariakit/react';
/**
* WordPress dependencies
*/
-import { forwardRef } from '@wordpress/element';
+import { useMemo, forwardRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import type { WordPressComponentProps } from '../context';
+import { CompositeContext, useCompositeContext } from './context';
import type {
CompositeStoreProps,
CompositeProps,
@@ -72,7 +73,14 @@ const Group = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeGroupProps, 'div', false >
>( function CompositeGroup( props, ref ) {
- return ;
+ const context = useCompositeContext();
+ return (
+
+ );
} );
Group.displayName = 'Composite.Group';
@@ -80,7 +88,14 @@ const GroupLabel = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeGroupLabelProps, 'div', false >
>( function CompositeGroupLabel( props, ref ) {
- return ;
+ const context = useCompositeContext();
+ return (
+
+ );
} );
GroupLabel.displayName = 'Composite.GroupLabel';
@@ -88,7 +103,14 @@ const Item = forwardRef<
HTMLButtonElement,
WordPressComponentProps< CompositeItemProps, 'button', false >
>( function CompositeItem( props, ref ) {
- return ;
+ const context = useCompositeContext();
+ return (
+
+ );
} );
Item.displayName = 'Composite.Item';
@@ -96,7 +118,14 @@ const Row = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeRowProps, 'div', false >
>( function CompositeRow( props, ref ) {
- return ;
+ const context = useCompositeContext();
+ return (
+
+ );
} );
Row.displayName = 'Composite.Row';
@@ -104,7 +133,14 @@ const Hover = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeHoverProps, 'div', false >
>( function CompositeHover( props, ref ) {
- return ;
+ const context = useCompositeContext();
+ return (
+
+ );
} );
Hover.displayName = 'Composite.Hover';
@@ -112,7 +148,14 @@ const Typeahead = forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeTypeaheadProps, 'div', false >
>( function CompositeTypeahead( props, ref ) {
- return ;
+ const context = useCompositeContext();
+ return (
+
+ );
} );
Typeahead.displayName = 'Composite.Typeahead';
@@ -136,9 +179,28 @@ export const Composite = Object.assign(
forwardRef<
HTMLDivElement,
WordPressComponentProps< CompositeProps, 'div', false >
- >( function Composite( { disabled = false, ...props }, ref ) {
+ >( function Composite(
+ { children, store, disabled = false, ...props },
+ ref
+ ) {
+ const contextValue = useMemo(
+ () => ( {
+ store,
+ } ),
+ [ store ]
+ );
+
return (
-
+
+
+ { children }
+
+
);
} ),
{
@@ -260,5 +322,20 @@ export const Composite = Object.assign(
* ```
*/
Typeahead,
+ /**
+ * The React context used by the composite components. It can be used by
+ * to access the composite store, and to forward the context when composite
+ * sub-components are rendered across portals (ie. `SlotFill` components)
+ * that would not otherwise forward the context to the `Fill` children.
+ *
+ * @example
+ * ```jsx
+ * import { Composite } from '@wordpress/components';
+ * import { useContext } from '@wordpress/element';
+ *
+ * const compositeContext = useContext( Composite.Context );
+ * ```
+ */
+ Context: CompositeContext,
}
);
diff --git a/packages/components/src/composite/types.ts b/packages/components/src/composite/types.ts
index 5afe410f7582ba..05a2b8473eb349 100644
--- a/packages/components/src/composite/types.ts
+++ b/packages/components/src/composite/types.ts
@@ -3,6 +3,15 @@
*/
import type * as Ariakit from '@ariakit/react';
+export type CompositeContextProps =
+ | {
+ /**
+ * Object returned by the `useCompositeStore` hook.
+ */
+ store: Ariakit.CompositeStore;
+ }
+ | undefined;
+
export type CompositeStoreProps = {
/**
* The current active item `id`. The active item is the element within the