diff --git a/.changeset/tame-carpets-strive.md b/.changeset/tame-carpets-strive.md
new file mode 100644
index 000000000..ef6eaef06
--- /dev/null
+++ b/.changeset/tame-carpets-strive.md
@@ -0,0 +1,6 @@
+---
+'@radix-ui/react-collapsible': minor
+'@radix-ui/react-accordion': minor
+---
+
+add prop keepChildrenMounted
diff --git a/apps/storybook/stories/accordion.stories.tsx b/apps/storybook/stories/accordion.stories.tsx
index 6ebe7e99c..5e8ba742e 100644
--- a/apps/storybook/stories/accordion.stories.tsx
+++ b/apps/storybook/stories/accordion.stories.tsx
@@ -492,6 +492,94 @@ export const Horizontal = () => (
>
);
+export const KeepChildrenMounted = () => (
+ <>
+
Single/Uncontrolled
+
+
+
+ One
+
+
+ Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra
+ integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit
+ habitant sed.
+
+
+
+
+ Two
+
+
+ Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum
+ porta nascetur ac dictum, leo tellus dis integer platea ultrices mi.
+
+
+
+
+ Three (disabled)
+
+
+ Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos
+ euismod magna, nec tempor pulvinar eu etiam mattis.
+
+
+
+
+ Four
+
+
+ Odio placerat quisque sapien sagittis non sociis ligula penatibus
+ dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero.
+
+
+
+
+
+ Multiple/Uncontrolled
+
+
+
+ One
+
+
+ Per erat orci nostra luctus sociosqu mus risus penatibus, duis elit vulputate viverra
+ integer ullamcorper congue curabitur sociis, nisi malesuada scelerisque quam suscipit
+ habitant sed.
+
+
+
+
+ Two
+
+
+ Cursus sed mattis commodo fermentum conubia ipsum pulvinar sagittis, diam eget bibendum
+ porta nascetur ac dictum, leo tellus dis integer platea ultrices mi.
+
+
+
+
+ Three (disabled)
+
+
+ Sociis hac sapien turpis conubia sagittis justo dui, inceptos penatibus feugiat himenaeos
+ euismod magna, nec tempor pulvinar eu etiam mattis.
+
+
+
+
+ Four
+
+
+ Odio placerat quisque sapien sagittis non sociis ligula penatibus
+ dignissim vitae, enim vulputate nullam semper potenti etiam volutpat libero.
+
+
+
+
+ >
+);
+
export const Chromatic = () => {
const items = ['One', 'Two', 'Three', 'Four'];
return (
diff --git a/apps/storybook/stories/collapsible.stories.tsx b/apps/storybook/stories/collapsible.stories.tsx
index 64cbcf89a..021fed629 100644
--- a/apps/storybook/stories/collapsible.stories.tsx
+++ b/apps/storybook/stories/collapsible.stories.tsx
@@ -58,6 +58,16 @@ export const AnimatedHorizontal = () => {
);
};
+export const KeepChildrenMounted = () => (
+ <>
+ With keepChildrenMounted
+
+ Trigger
+ Content 1
+
+ >
+);
+
export const Chromatic = () => (
<>
Uncontrolled
diff --git a/packages/react/accordion/src/accordion.tsx b/packages/react/accordion/src/accordion.tsx
index 4ec048265..d2e25fe88 100644
--- a/packages/react/accordion/src/accordion.tsx
+++ b/packages/react/accordion/src/accordion.tsx
@@ -196,6 +196,7 @@ type AccordionImplContextValue = {
disabled?: boolean;
direction: AccordionImplProps['dir'];
orientation: AccordionImplProps['orientation'];
+ keepChildrenMounted?: boolean;
};
const [AccordionImplProvider, useAccordionContext] =
@@ -219,11 +220,23 @@ interface AccordionImplProps extends PrimitiveDivProps {
* The language read direction.
*/
dir?: Direction;
+ /**
+ * When set to true, children of content will remain mounted when content is
+ * collapsed.
+ */
+ keepChildrenMounted?: boolean;
}
const AccordionImpl = React.forwardRef(
(props: ScopedProps, forwardedRef) => {
- const { __scopeAccordion, disabled, dir, orientation = 'vertical', ...accordionProps } = props;
+ const {
+ __scopeAccordion,
+ disabled,
+ dir,
+ orientation = 'vertical',
+ keepChildrenMounted,
+ ...accordionProps
+ } = props;
const accordionRef = React.useRef(null);
const composedRefs = useComposedRefs(accordionRef, forwardedRef);
const getItems = useCollection(__scopeAccordion);
@@ -307,6 +320,7 @@ const AccordionImpl = React.forwardRef
disabled={disabled}
direction={dir}
orientation={orientation}
+ keepChildrenMounted={keepChildrenMounted}
>
{...collapsibleScope}
{...accordionItemProps}
ref={forwardedRef}
+ keepChildrenMounted={accordionContext.keepChildrenMounted}
disabled={disabled}
open={open}
onOpenChange={(open) => {
diff --git a/packages/react/collapsible/src/collapsible.test.tsx b/packages/react/collapsible/src/collapsible.test.tsx
index e5e6c53ee..51f2295c3 100644
--- a/packages/react/collapsible/src/collapsible.test.tsx
+++ b/packages/react/collapsible/src/collapsible.test.tsx
@@ -108,3 +108,41 @@ describe('given an open controlled Collapsible', () => {
});
});
});
+
+describe('given a collapsible with keepChildrenMounted', () => {
+ let rendered: RenderResult;
+ let content: HTMLElement;
+
+ afterEach(cleanup);
+
+ beforeEach(() => {
+ rendered = render();
+ content = rendered.getByText(CONTENT_TEXT);
+ });
+
+ describe('when clicking the trigger', () => {
+ beforeEach(() => {
+ const trigger = rendered.getByText(TRIGGER_TEXT);
+ fireEvent.click(trigger);
+ });
+
+ it('should show the content', () => {
+ expect(content).toBeVisible();
+ })
+
+ describe('and clicking the trigger again', () => {
+ beforeEach(() => {
+ const trigger = rendered.getByText(TRIGGER_TEXT);
+ fireEvent.click(trigger);
+ });
+
+ it('should close the content', () => {
+ expect(content).not.toBeVisible();
+ });
+
+ it('should keep the children mounted', () => {
+ expect(content).toBeInTheDocument();
+ });
+ });
+ });
+})
diff --git a/packages/react/collapsible/src/collapsible.tsx b/packages/react/collapsible/src/collapsible.tsx
index 3e7f47b55..3452b14e6 100644
--- a/packages/react/collapsible/src/collapsible.tsx
+++ b/packages/react/collapsible/src/collapsible.tsx
@@ -21,6 +21,7 @@ const [createCollapsibleContext, createCollapsibleScope] = createContextScope(CO
type CollapsibleContextValue = {
contentId: string;
+ keepChildrenMounted?: boolean;
disabled?: boolean;
open: boolean;
onOpenToggle(): void;
@@ -33,6 +34,11 @@ type CollapsibleElement = React.ComponentRef;
type PrimitiveDivProps = React.ComponentPropsWithoutRef;
interface CollapsibleProps extends PrimitiveDivProps {
defaultOpen?: boolean;
+ /**
+ * When set to true, children of content will remain mounted when content is
+ * collapsed.
+ */
+ keepChildrenMounted?: boolean;
open?: boolean;
disabled?: boolean;
onOpenChange?(open: boolean): void;
@@ -44,6 +50,7 @@ const Collapsible = React.forwardRef(
__scopeCollapsible,
open: openProp,
defaultOpen,
+ keepChildrenMounted,
disabled,
onOpenChange,
...collapsibleProps
@@ -59,6 +66,7 @@ const Collapsible = React.forwardRef(
return (
- {isOpen && children}
+ {(isOpen || context.keepChildrenMounted) && children}
);
});