Skip to content

bug(aria/accordion): preserveContent input causes NG3004 in library builds #32590

@LayZeeDK

Description

@LayZeeDK

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

When building an Angular library (using partial compilation mode / ng-packagr), binding to the preserveContent input on AccordionPanel causes the following error:

NG3004: Unable to import symbol DeferredContentAware.
The symbol is not exported from @angular/aria/types/accordion.d.ts (module '@angular/aria/accordion').

Root Cause Analysis

The AccordionPanel directive uses hostDirectives to expose DeferredContentAware:

// From @angular/aria/types/accordion.d.ts (line 53)
static ɵdir: ɵɵDirectiveDeclaration<AccordionPanel, "[ngAccordionPanel]", ...,
  [{ directive: typeof DeferredContentAware; inputs: { "preserveContent": "preserveContent"; }; outputs: {}; }]>;

The preserveContent input IS exposed via the hostDirectives inputs mapping, making it appear as a valid input on AccordionPanel. However, when Angular's AOT compiler type-checks a template binding like [preserveContent]="true", it needs to import the DeferredContentAware type to verify the binding.

The problem is that DeferredContentAware is imported from an internal module:

// From @angular/aria/types/accordion.d.ts (line 4)
import { DeferredContentAware } from '@angular/aria/_deferred-content-chunk.js';

The underscore prefix (_) indicates this is a private/internal module, and DeferredContentAware is not re-exported from @angular/aria/accordion.

The Inconsistency

  1. AccordionPanel exposes preserveContent as a usable input via hostDirectives
  2. But the type required to use this input (DeferredContentAware) is not exported
  3. This works in JIT mode (development) but fails in AOT/partial compilation (library builds)

Reproduction

StackBlitz link: Unable to reproduce in StackBlitz as this requires a library build with ng-packagr (partial compilation mode). StackBlitz runs in JIT mode where the issue doesn't manifest.

  1. Create an Angular library project using ng-packagr
  2. Create a component that uses AccordionPanel with preserveContent binding:
@Component({
  selector: 'my-accordion',
  imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
  template: `
    <div ngAccordionGroup>
      <button ngAccordionTrigger [panelId]="'panel-1'">Toggle</button>
      <div ngAccordionPanel
           [panelId]="'panel-1'"
           [preserveContent]="true">  <!-- THIS CAUSES NG3004 -->
        <ng-template ngAccordionContent>
          <p>Panel content</p>
        </ng-template>
      </div>
    </div>
  `
})
export class MyAccordionComponent {}
  1. Build the library:
ng build my-library
# or
npx ng-packagr -p ng-package.json
  1. Observe the error:
NG3004: Unable to import symbol DeferredContentAware.
The symbol is not exported from @angular/aria/types/accordion.d.ts (module '@angular/aria/accordion').

Expected Behavior

One of the following:

Option A: Export DeferredContentAware (Preferred)

Export DeferredContentAware from @angular/aria/accordion so that consumers can use the preserveContent input that is already exposed via hostDirectives:

// @angular/aria/accordion/index.ts
export { DeferredContentAware } from '../_deferred-content-chunk';
export { AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent } from './accordion';

Option B: Document the limitation

If preserveContent is intentionally internal, remove it from the hostDirectives inputs mapping or document that it cannot be used in library builds.

Actual Behavior

  • The preserveContent input appears to be available (TypeScript/IDE shows it as a valid input)
  • Development mode (JIT) works fine
  • Library builds (AOT partial compilation) fail with NG3004
  • There is no workaround - attribute binding [attr.preserveContent]="true" bypasses the error but doesn't actually set the signal input value

Environment

Angular CLI: 21.0.4
Node: 24.12.0
Package Manager: npm 11.6.2
OS: win32 arm64

Angular: 21.0.6
@angular/aria: 21.0.5
@angular/cdk: 21.0.5

Browser(s): Chrome, Firefox, Safari (not browser-specific)
Operating System: Windows 11 ARM64 (also reproducible on other platforms)

Additional Context

Why preserveContent is needed

Without preserveContent=true, there can be content duplication issues when combining:

  • ngAccordionContent (deferred content)
  • NgTemplateOutlet (for user-provided content templates)
  • Native Angular animations (animate.enter/animate.leave)

When the panel visibility changes, the deferred content system destroys/recreates views, which can conflict with animation lifecycle and NgTemplateOutlet, resulting in duplicate content appearing in the DOM.

Current Workaround

The only workaround is to not use preserveContent and avoid combining deferred content with animations - but this limits the functionality that Angular ARIA accordion can provide.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs triageThis issue needs to be triaged by the team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions